diff --git a/.github/actions/build-jtreg/action.yml b/.github/actions/build-jtreg/action.yml index a9c046e9dd9..334812e8341 100644 --- a/.github/actions/build-jtreg/action.yml +++ b/.github/actions/build-jtreg/action.yml @@ -37,13 +37,13 @@ runs: - name: 'Check cache for already built JTReg' id: get-cached - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: jtreg/installed key: jtreg-${{ steps.version.outputs.value }} - name: 'Checkout the JTReg source' - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: repository: openjdk/jtreg ref: jtreg-${{ steps.version.outputs.value }} @@ -61,7 +61,7 @@ runs: if: (steps.get-cached.outputs.cache-hit != 'true') - name: 'Upload JTReg artifact' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: bundles-jtreg-${{ steps.version.outputs.value }} path: jtreg/installed diff --git a/.github/actions/do-build/action.yml b/.github/actions/do-build/action.yml index 6f2c2ce0218..6f6bbdabb68 100644 --- a/.github/actions/do-build/action.yml +++ b/.github/actions/do-build/action.yml @@ -66,7 +66,7 @@ runs: shell: bash - name: 'Upload build logs' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: failure-logs-${{ inputs.platform }}${{ inputs.debug-suffix }} path: failure-logs @@ -74,7 +74,7 @@ runs: # This is the best way I found to abort the job with an error message - name: 'Notify about build failures' - uses: actions/github-script@v7 + uses: actions/github-script@v8 with: script: core.setFailed('Build failed. See summary for details.') if: steps.check.outputs.failure == 'true' diff --git a/.github/actions/get-bootjdk/action.yml b/.github/actions/get-bootjdk/action.yml index 312fb642c82..d531358b7dd 100644 --- a/.github/actions/get-bootjdk/action.yml +++ b/.github/actions/get-bootjdk/action.yml @@ -65,7 +65,7 @@ runs: - name: 'Check cache for BootJDK' id: get-cached-bootjdk - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: bootjdk/jdk key: boot-jdk-${{ inputs.platform }}-${{ steps.sha256.outputs.value }} diff --git a/.github/actions/get-bundles/action.yml b/.github/actions/get-bundles/action.yml index a356aa9fd8d..55fa0e842d2 100644 --- a/.github/actions/get-bundles/action.yml +++ b/.github/actions/get-bundles/action.yml @@ -54,14 +54,14 @@ runs: steps: - name: 'Download bundles artifact' id: download-bundles - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: bundles-${{ inputs.platform }}${{ inputs.debug-suffix }} path: bundles continue-on-error: true - name: 'Download bundles artifact (retry)' - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: bundles-${{ inputs.platform }}${{ inputs.debug-suffix }} path: bundles @@ -69,7 +69,7 @@ runs: - name: 'Download static bundles artifact' id: download-static-bundles - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: bundles-${{ inputs.platform }}${{ inputs.debug-suffix }}${{ inputs.static-suffix }} path: bundles diff --git a/.github/actions/get-gtest/action.yml b/.github/actions/get-gtest/action.yml index 7a329460a6e..bc53fa2a3b1 100644 --- a/.github/actions/get-gtest/action.yml +++ b/.github/actions/get-gtest/action.yml @@ -40,7 +40,7 @@ runs: var: GTEST_VERSION - name: 'Checkout GTest source' - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: repository: google/googletest ref: 'v${{ steps.version.outputs.value }}' diff --git a/.github/actions/get-jtreg/action.yml b/.github/actions/get-jtreg/action.yml index 36c895fc59d..8c75ae10c7f 100644 --- a/.github/actions/get-jtreg/action.yml +++ b/.github/actions/get-jtreg/action.yml @@ -41,7 +41,7 @@ runs: - name: 'Download JTReg artifact' id: download-jtreg - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: bundles-jtreg-${{ steps.version.outputs.value }} path: jtreg/installed diff --git a/.github/actions/get-msys2/action.yml b/.github/actions/get-msys2/action.yml index 308230ebf2e..7351a120ac4 100644 --- a/.github/actions/get-msys2/action.yml +++ b/.github/actions/get-msys2/action.yml @@ -31,7 +31,7 @@ runs: steps: - name: 'Install MSYS2' id: msys2 - uses: msys2/setup-msys2@v2.28.0 + uses: msys2/setup-msys2@v2.31.0 with: install: 'autoconf tar unzip zip make' path-type: minimal diff --git a/.github/actions/upload-bundles/action.yml b/.github/actions/upload-bundles/action.yml index 78fb0a94bfd..94308002ea7 100644 --- a/.github/actions/upload-bundles/action.yml +++ b/.github/actions/upload-bundles/action.yml @@ -87,7 +87,7 @@ runs: shell: bash - name: 'Upload bundles artifact' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: bundles-${{ inputs.platform }}${{ inputs.debug-suffix }}${{ inputs.static-suffix }}${{ inputs.bundle-suffix }} path: bundles diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000000..d3f63784ece --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,5 @@ + + + +--------- +- [ ] I confirm that I make this contribution in accordance with the [OpenJDK Interim AI Policy](https://openjdk.org/legal/ai). diff --git a/.github/workflows/build-alpine-linux.yml b/.github/workflows/build-alpine-linux.yml index c39962fa07f..6863da9016e 100644 --- a/.github/workflows/build-alpine-linux.yml +++ b/.github/workflows/build-alpine-linux.yml @@ -74,7 +74,7 @@ jobs: steps: - name: 'Checkout the JDK source' - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: 'Install toolchain and dependencies' run: | diff --git a/.github/workflows/build-cross-compile.yml b/.github/workflows/build-cross-compile.yml index a0642d469aa..99b6c40606c 100644 --- a/.github/workflows/build-cross-compile.yml +++ b/.github/workflows/build-cross-compile.yml @@ -94,7 +94,7 @@ jobs: steps: - name: 'Checkout the JDK source' - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: 'Get the BootJDK' id: bootjdk @@ -122,7 +122,7 @@ jobs: - name: 'Check cache for sysroot' id: get-cached-sysroot - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: sysroot key: sysroot-${{ matrix.debian-arch }}-${{ hashFiles('./.github/workflows/build-cross-compile.yml') }} diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index 791b53a3f04..c501670439e 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -84,7 +84,7 @@ jobs: steps: - name: 'Checkout the JDK source' - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: 'Get the BootJDK' id: bootjdk diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index 484e616fad7..435576f4afd 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -75,7 +75,7 @@ jobs: steps: - name: 'Checkout the JDK source' - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: 'Get the BootJDK' id: bootjdk diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index 4dafc016a99..3bb50a137ec 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -83,7 +83,7 @@ jobs: steps: - name: 'Checkout the JDK source' - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: 'Get MSYS2' uses: ./.github/actions/get-msys2 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 85ec75f343c..20be196b128 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -75,7 +75,7 @@ jobs: steps: - name: 'Checkout the scripts' - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: sparse-checkout: | .github diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8f33454305e..b240b42fb97 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -128,7 +128,7 @@ jobs: steps: - name: 'Checkout the JDK source' - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: 'Get MSYS2' uses: ./.github/actions/get-msys2 @@ -239,7 +239,7 @@ jobs: if: always() - name: 'Upload test results' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: path: results name: ${{ steps.package.outputs.artifact-name }} @@ -247,7 +247,7 @@ jobs: # This is the best way I found to abort the job with an error message - name: 'Notify about test failures' - uses: actions/github-script@v7 + uses: actions/github-script@v8 with: script: core.setFailed('${{ steps.run-tests.outputs.error-message }}') if: steps.run-tests.outputs.failure == 'true' diff --git a/.gitignore b/.gitignore index b6b4a1a559a..a45e2113756 100644 --- a/.gitignore +++ b/.gitignore @@ -24,8 +24,6 @@ NashornProfile.txt /.gdbinit /.lldbinit **/core.[0-9]* -*.rej -*.orig test/benchmarks/**/target /src/hotspot/CMakeLists.txt /src/hotspot/compile_commands.json diff --git a/bin/idea.sh b/bin/idea.sh index a184884b61a..d9a18956e3b 100644 --- a/bin/idea.sh +++ b/bin/idea.sh @@ -187,14 +187,18 @@ fi SOURCE_PREFIX="" +# SOURCES is a single string containing embeded newlines. for root in $MODULE_ROOTS; do if [ "x$CYGPATH" != "x" ]; then root=`$CYGPATH -am $root` elif [ "x$WSL_DISTRO_NAME" != "x" ]; then root=`wslpath -am $root` fi - - SOURCES=$SOURCES" $SOURCE_PREFIX""$root""$SOURCE_POSTFIX" + # Add line termination/indentation for everything after the first entry. + if [ "x$SOURCES" != "x" ]; then + SOURCES="${SOURCES}\n " + fi + SOURCES="${SOURCES}${SOURCE_PREFIX}${root}${SOURCE_POSTFIX}" done add_replacement "###SOURCE_ROOTS###" "$SOURCES" diff --git a/doc/testing.html b/doc/testing.html index 195153c8612..c8d0b928bb0 100644 --- a/doc/testing.html +++ b/doc/testing.html @@ -284,9 +284,10 @@ possible, or if you want to use a fully qualified test descriptor, add

Gtest

Note: To be able to run the Gtest suite, you need to configure your build to be able to find a proper version of the gtest -source. For details, see the section "Running Tests" in the build -documentation.

+source. For details, see the section "Running Tests" in the +build documentation (html, markdown).

Since the Hotspot Gtest suite is so quick, the default is to run all tests. This is specified by just gtest, or as a fully qualified test descriptor gtest:all.

diff --git a/doc/testing.md b/doc/testing.md index d0e54aab02b..5f70f2796ad 100644 --- a/doc/testing.md +++ b/doc/testing.md @@ -198,8 +198,8 @@ use a fully qualified test descriptor, add `jtreg:`, e.g. **Note:** To be able to run the Gtest suite, you need to configure your build to be able to find a proper version of the gtest source. For details, see the -section ["Running Tests" in the build -documentation](building.html#running-tests). +section **"Running Tests" in the build +documentation** ([html](building.html#running-tests), [markdown](building.md#running-tests)). Since the Hotspot Gtest suite is so quick, the default is to run all tests. This is specified by just `gtest`, or as a fully qualified test descriptor diff --git a/make/Docs.gmk b/make/Docs.gmk index a8d40e078e2..9cee8cd40c1 100644 --- a/make/Docs.gmk +++ b/make/Docs.gmk @@ -1,4 +1,4 @@ -# Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 1997, 2026, 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 @@ -93,19 +93,16 @@ JAVADOC_DISABLED_DOCLINT_WARNINGS := missing JAVADOC_DISABLED_DOCLINT_PACKAGES := org.w3c.* javax.smartcardio # The initial set of options for javadoc -# -XDaccessInternalAPI is a temporary workaround, see 8373909 JAVADOC_OPTIONS := -use -keywords -notimestamp \ -serialwarn -encoding utf-8 -docencoding utf-8 -breakiterator \ -splitIndex --system none -javafx --expand-requires transitive \ - --override-methods=summary \ - -XDaccessInternalAPI + --override-methods=summary # The reference options must stay stable to allow for comparisons across the # development cycle. REFERENCE_OPTIONS := -XDignore.symbol.file=true -use -keywords -notimestamp \ -serialwarn -encoding utf-8 -breakiterator -splitIndex --system none \ - -html5 -javafx --expand-requires transitive \ - -XDaccessInternalAPI + -html5 -javafx --expand-requires transitive # Should we add DRAFT stamps to the generated javadoc? ifeq ($(VERSION_IS_GA), true) diff --git a/make/Hsdis.gmk b/make/Hsdis.gmk index 469cc488f16..76695fc8dde 100644 --- a/make/Hsdis.gmk +++ b/make/Hsdis.gmk @@ -44,6 +44,9 @@ ifeq ($(HSDIS_BACKEND), capstone) else ifeq ($(call isTargetCpuArch, aarch64), true) CAPSTONE_ARCH := CS_ARCH_$(CAPSTONE_ARCH_AARCH64_NAME) CAPSTONE_MODE := CS_MODE_ARM + else ifeq ($(call isTargetCpuArch, arm), true) + CAPSTONE_ARCH := CS_ARCH_ARM + CAPSTONE_MODE := CS_MODE_ARM else $(error No support for Capstone on this platform) endif diff --git a/make/RunTests.gmk b/make/RunTests.gmk index 02ea632e3ec..d4be5936c41 100644 --- a/make/RunTests.gmk +++ b/make/RunTests.gmk @@ -1020,6 +1020,9 @@ define SetupRunJtregTestBody VM_OPTIONS := $$(JTREG_ALL_OPTIONS) )) $$(call LogWarn, AOT_JDK_CACHE=$$($1_AOT_JDK_CACHE)) $1_JTREG_BASIC_OPTIONS += -vmoption:-XX:AOTCache="$$($1_AOT_JDK_CACHE)" + $1_JTREG_BASIC_OPTIONS += $$(addprefix $$(JTREG_PROBLEM_LIST_PREFIX), $$(wildcard \ + $$(addprefix $$($1_TEST_ROOT)/, ProblemList-AotJdk.txt) \ + )) endif diff --git a/make/autoconf/flags-cflags.m4 b/make/autoconf/flags-cflags.m4 index ab9cd8be19b..423654cd50a 100644 --- a/make/autoconf/flags-cflags.m4 +++ b/make/autoconf/flags-cflags.m4 @@ -544,12 +544,9 @@ AC_DEFUN([FLAGS_SETUP_CFLAGS_HELPER], TOOLCHAIN_CFLAGS_JVM="$TOOLCHAIN_CFLAGS_JVM -fstack-protector" TOOLCHAIN_CFLAGS_JDK="-fvisibility=hidden -pipe -fstack-protector" # reduce lib size on linux in link step, this needs also special compile flags - # do this on s390x also for libjvm (where serviceability agent is not supported) if test "x$ENABLE_LINKTIME_GC" = xtrue; then TOOLCHAIN_CFLAGS_JDK="$TOOLCHAIN_CFLAGS_JDK -ffunction-sections -fdata-sections" - if test "x$OPENJDK_TARGET_CPU" = xs390x && test "x$DEBUG_LEVEL" == xrelease; then - TOOLCHAIN_CFLAGS_JVM="$TOOLCHAIN_CFLAGS_JVM -ffunction-sections -fdata-sections" - fi + TOOLCHAIN_CFLAGS_JVM="$TOOLCHAIN_CFLAGS_JVM -ffunction-sections -fdata-sections" fi # technically NOT for CXX (but since this gives *worse* performance, use # no-strict-aliasing everywhere!) diff --git a/make/autoconf/flags-ldflags.m4 b/make/autoconf/flags-ldflags.m4 index 7782735be25..ff10828731e 100644 --- a/make/autoconf/flags-ldflags.m4 +++ b/make/autoconf/flags-ldflags.m4 @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2026, 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,16 +53,15 @@ AC_DEFUN([FLAGS_SETUP_LDFLAGS_HELPER], # add --icf=all (Identical Code Folding — merges identical functions) BASIC_LDFLAGS="-Wl,-z,defs -Wl,-z,relro -Wl,-z,now -Wl,--no-as-needed -Wl,--exclude-libs,ALL" + BASIC_LDFLAGS_JVM_ONLY="" # Linux : remove unused code+data in link step if test "x$ENABLE_LINKTIME_GC" = xtrue; then - if test "x$OPENJDK_TARGET_CPU" = xs390x; then - BASIC_LDFLAGS="$BASIC_LDFLAGS -Wl,--gc-sections" - else - BASIC_LDFLAGS_JDK_ONLY="$BASIC_LDFLAGS_JDK_ONLY -Wl,--gc-sections" - fi + # keep vtables : -Wl,--undefined-glob=_ZTV* (but this seems not to work with gold ld) + # so keep at least the Metadata vtable that is used in the serviceability agent + BASIC_LDFLAGS_JVM_ONLY="$BASIC_LDFLAGS_JVM_ONLY -Wl,--gc-sections -Wl,--undefined=_ZTV8Metadata" + BASIC_LDFLAGS_JDK_ONLY="$BASIC_LDFLAGS_JDK_ONLY -Wl,--gc-sections" fi - BASIC_LDFLAGS_JVM_ONLY="" LDFLAGS_LTO="-flto=auto -fuse-linker-plugin -fno-strict-aliasing $DEBUG_PREFIX_CFLAGS" LDFLAGS_CXX_PARTIAL_LINKING="$MACHINE_FLAG -r" diff --git a/make/autoconf/toolchain_microsoft.m4 b/make/autoconf/toolchain_microsoft.m4 index f577cf1a2a1..afe04acf029 100644 --- a/make/autoconf/toolchain_microsoft.m4 +++ b/make/autoconf/toolchain_microsoft.m4 @@ -217,10 +217,12 @@ AC_DEFUN([TOOLCHAIN_FIND_VISUAL_STUDIO_BAT_FILE], TOOLCHAIN_CHECK_POSSIBLE_VISUAL_STUDIO_ROOT([$TARGET_CPU], [$VS_VERSION], [$PROGRAMFILES_X86/$VS_INSTALL_DIR], [well-known name]) fi + # Derive system drive root from CMD (which is at /windows/system32/cmd.exe) + WINSYSDRIVE_ROOT="$(dirname "$(dirname "$(dirname "$CMD")")")" TOOLCHAIN_CHECK_POSSIBLE_VISUAL_STUDIO_ROOT([$TARGET_CPU], [$VS_VERSION], - [c:/program files/$VS_INSTALL_DIR], [well-known name]) + [$WINSYSDRIVE_ROOT/program files/$VS_INSTALL_DIR], [well-known name]) TOOLCHAIN_CHECK_POSSIBLE_VISUAL_STUDIO_ROOT([$TARGET_CPU], [$VS_VERSION], - [c:/program files (x86)/$VS_INSTALL_DIR], [well-known name]) + [$WINSYSDRIVE_ROOT/program files (x86)/$VS_INSTALL_DIR], [well-known name]) if test "x$SDK_INSTALL_DIR" != x; then if test "x$ProgramW6432" != x; then TOOLCHAIN_CHECK_POSSIBLE_WIN_SDK_ROOT([$TARGET_CPU], [$VS_VERSION], @@ -235,9 +237,9 @@ AC_DEFUN([TOOLCHAIN_FIND_VISUAL_STUDIO_BAT_FILE], [$PROGRAMFILES/$SDK_INSTALL_DIR], [well-known name]) fi TOOLCHAIN_CHECK_POSSIBLE_WIN_SDK_ROOT([$TARGET_CPU], [$VS_VERSION], - [c:/program files/$SDK_INSTALL_DIR], [well-known name]) + [$WINSYSDRIVE_ROOT/program files/$SDK_INSTALL_DIR], [well-known name]) TOOLCHAIN_CHECK_POSSIBLE_WIN_SDK_ROOT([$TARGET_CPU], [$VS_VERSION], - [c:/program files (x86)/$SDK_INSTALL_DIR], [well-known name]) + [$WINSYSDRIVE_ROOT/program files (x86)/$SDK_INSTALL_DIR], [well-known name]) fi VCVARS_VER=auto @@ -338,7 +340,7 @@ AC_DEFUN([TOOLCHAIN_EXTRACT_VISUAL_STUDIO_ENV], OLDPATH="$PATH" # Make sure we only capture additions to PATH needed by VS. # Clear out path, but need system dir present for vsvars cmd file to be able to run - export PATH=$WINENV_PREFIX/c/windows/system32 + export PATH="$(dirname "$CMD")" # The "| cat" is to stop SetEnv.Cmd to mess with system colors on some systems # We can't pass -vcvars_ver=$VCVARS_VER here because cmd.exe eats all '=' # in bat file arguments. :-( diff --git a/make/common/modules/LauncherCommon.gmk b/make/common/modules/LauncherCommon.gmk index 859494861b2..8d45142ef4a 100644 --- a/make/common/modules/LauncherCommon.gmk +++ b/make/common/modules/LauncherCommon.gmk @@ -36,16 +36,16 @@ include $(TOPDIR)/make/ToolsJdk.gmk LAUNCHER_SRC := $(TOPDIR)/src/java.base/share/native/launcher -ifeq ($(call isTargetOs, aix), true) - ADD_PLATFORM_INCLUDE_DIR := -I$(TOPDIR)/src/java.base/aix/native/include -endif - LAUNCHER_CFLAGS += -I$(TOPDIR)/src/java.base/share/native/launcher \ -I$(TOPDIR)/src/java.base/share/native/libjli \ - $(ADD_PLATFORM_INCLUDE_DIR) \ -I$(TOPDIR)/src/java.base/$(OPENJDK_TARGET_OS_TYPE)/native/libjli \ -I$(TOPDIR)/src/java.base/$(OPENJDK_TARGET_OS)/native/libjli \ # + +ifeq ($(call isTargetOs, aix), true) + LAUNCHER_CFLAGS += -I$(TOPDIR)/src/java.base/aix/native/include +endif + MACOSX_PLIST_DIR := $(TOPDIR)/src/java.base/macosx/native/launcher JAVA_MANIFEST := $(TOPDIR)/src/java.base/windows/native/launcher/java.manifest diff --git a/make/conf/github-actions.conf b/make/conf/github-actions.conf index ebfc9191535..6771e8923dc 100644 --- a/make/conf/github-actions.conf +++ b/make/conf/github-actions.conf @@ -29,21 +29,21 @@ GTEST_VERSION=1.14.0 JTREG_VERSION=8.2.1+1 LINUX_X64_BOOT_JDK_EXT=tar.gz -LINUX_X64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk25/bd75d5f9689641da8e1daabeccb5528b/36/GPL/openjdk-25_linux-x64_bin.tar.gz -LINUX_X64_BOOT_JDK_SHA256=59cdcaf255add4721de38eb411d4ecfe779356b61fb671aee63c7dec78054c2b +LINUX_X64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk26/c3cc523845074aa0af4f5e1e1ed4151d/35/GPL/openjdk-26_linux-x64_bin.tar.gz +LINUX_X64_BOOT_JDK_SHA256=83c78367f8c81257beef72aca4bbbf8e6dac8ca2b3a4546a85879a09e6e4e128 ALPINE_LINUX_X64_BOOT_JDK_EXT=tar.gz -ALPINE_LINUX_X64_BOOT_JDK_URL=https://github.com/adoptium/temurin25-binaries/releases/download/jdk-25%2B36/OpenJDK25U-jdk_x64_alpine-linux_hotspot_25_36.tar.gz -ALPINE_LINUX_X64_BOOT_JDK_SHA256=637e47474d411ed86134f413af7d5fef4180ddb0bf556347b7e74a88cf8904c8 +ALPINE_LINUX_X64_BOOT_JDK_URL=https://github.com/adoptium/temurin26-binaries/releases/download/jdk-26%2B35/OpenJDK26U-jdk_x64_alpine-linux_hotspot_26_35.tar.gz +ALPINE_LINUX_X64_BOOT_JDK_SHA256=c105e581fdccb4e7120d889235d1ad8d5b2bed0af4972bc881e0a8ba687c94a4 MACOS_AARCH64_BOOT_JDK_EXT=tar.gz -MACOS_AARCH64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk25/bd75d5f9689641da8e1daabeccb5528b/36/GPL/openjdk-25_macos-aarch64_bin.tar.gz -MACOS_AARCH64_BOOT_JDK_SHA256=2006337bf326fdfdf6117081751ba38c1c8706d63419ecac7ff102ff7c776876 +MACOS_AARCH64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk26/c3cc523845074aa0af4f5e1e1ed4151d/35/GPL/openjdk-26_macos-aarch64_bin.tar.gz +MACOS_AARCH64_BOOT_JDK_SHA256=254586bcd1bf6dcd125ad667ac32562cb1e2ab1abf3a61fb117b6fabb571e765 MACOS_X64_BOOT_JDK_EXT=tar.gz -MACOS_X64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk25/bd75d5f9689641da8e1daabeccb5528b/36/GPL/openjdk-25_macos-x64_bin.tar.gz -MACOS_X64_BOOT_JDK_SHA256=47482ad9888991ecac9b2bcc131e2b53ff78aff275104cef85f66252308e8a09 +MACOS_X64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk26/c3cc523845074aa0af4f5e1e1ed4151d/35/GPL/openjdk-26_macos-x64_bin.tar.gz +MACOS_X64_BOOT_JDK_SHA256=8642b89d889c14ede2c446fd5bbe3621c8a3082e3df02013fd1658e39f52929a WINDOWS_X64_BOOT_JDK_EXT=zip -WINDOWS_X64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk25/bd75d5f9689641da8e1daabeccb5528b/36/GPL/openjdk-25_windows-x64_bin.zip -WINDOWS_X64_BOOT_JDK_SHA256=85bcc178461e2cb3c549ab9ca9dfa73afd54c09a175d6510d0884071867137d3 +WINDOWS_X64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk26/c3cc523845074aa0af4f5e1e1ed4151d/35/GPL/openjdk-26_windows-x64_bin.zip +WINDOWS_X64_BOOT_JDK_SHA256=2dd2d92c9374cd49a120fe9d916732840bf6bb9f0e0cc29794917a3c08b99c5f diff --git a/make/conf/jib-profiles.js b/make/conf/jib-profiles.js index 76a94b7789e..4c1d2835054 100644 --- a/make/conf/jib-profiles.js +++ b/make/conf/jib-profiles.js @@ -387,8 +387,8 @@ var getJibProfilesCommon = function (input, data) { }; }; - common.boot_jdk_version = "25"; - common.boot_jdk_build_number = "37"; + common.boot_jdk_version = "26"; + common.boot_jdk_build_number = "35"; common.boot_jdk_home = input.get("boot_jdk", "install_path") + "/jdk-" + common.boot_jdk_version + (input.build_os == "macosx" ? ".jdk/Contents/Home" : ""); diff --git a/make/conf/version-numbers.conf b/make/conf/version-numbers.conf index 4392d86ac33..4f63179ae05 100644 --- a/make/conf/version-numbers.conf +++ b/make/conf/version-numbers.conf @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2026, 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 @@ -37,6 +37,6 @@ DEFAULT_VERSION_DATE=2026-09-15 DEFAULT_VERSION_CLASSFILE_MAJOR=71 # "`$EXPR $DEFAULT_VERSION_FEATURE + 44`" DEFAULT_VERSION_CLASSFILE_MINOR=0 DEFAULT_VERSION_DOCS_API_SINCE=11 -DEFAULT_ACCEPTABLE_BOOT_VERSIONS="25 26 27" +DEFAULT_ACCEPTABLE_BOOT_VERSIONS="26 27" DEFAULT_JDK_SOURCE_TARGET_VERSION=27 DEFAULT_PROMOTED_VERSION_PRE=ea diff --git a/make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java b/make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java index ab878a4d2a5..9f42326ef09 100644 --- a/make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java +++ b/make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java @@ -87,6 +87,7 @@ public class CLDRConverter { static final String EXEMPLAR_CITY_PREFIX = "timezone.excity."; static final String ZONE_NAME_PREFIX = "timezone.displayname."; static final String METAZONE_ID_PREFIX = "metazone.id."; + static final String METAZONE_DSTOFFSET_PREFIX = "metazone.dstoffset."; static final String PARENT_LOCALE_PREFIX = "parentLocale."; static final String LIKELY_SCRIPT_PREFIX = "likelyScript."; static final String META_EMPTY_ZONE_NAME = "EMPTY_ZONE"; @@ -139,6 +140,11 @@ public class CLDRConverter { private static final Map tzdbSubstLetters = HashMap.newHashMap(512); private static final Map tzdbLinks = HashMap.newHashMap(512); + // Map of explicit dst offsets for metazones + // key: time zone ID + // value: explicit dstOffset for the corresponding metazone name + static final Map explicitDstOffsets = HashMap.newHashMap(32); + static enum DraftType { UNCONFIRMED, PROVISIONAL, @@ -795,10 +801,7 @@ public class CLDRConverter { String tzKey = Optional.ofNullable((String)handlerSupplMeta.get(tzid)) .orElse(tzid); // Follow link, if needed - String tzLink = null; - for (var k = tzKey; tzdbLinks.containsKey(k);) { - k = tzLink = tzdbLinks.get(k); - } + String tzLink = getTZDBLink(tzKey); if (tzLink == null && tzdbLinks.containsValue(tzKey)) { // reverse link search // this is needed as in tzdb, "America/Buenos_Aires" links to @@ -827,7 +830,7 @@ public class CLDRConverter { } else { // TZDB short names tznames = Arrays.copyOf(tznames, tznames.length); - fillTZDBShortNames(tzid, tznames); + fillTZDBShortNames(tzKey, tznames); names.put(tzid, tznames); } } else { @@ -840,11 +843,13 @@ public class CLDRConverter { String metaKey = METAZONE_ID_PREFIX + meta; data = map.get(metaKey); if (data instanceof String[] tznames) { - // TZDB short names - tznames = Arrays.copyOf((String[])names.getOrDefault(metaKey, tznames), 6); - fillTZDBShortNames(tzid, tznames); - // Keep the metazone prefix here. - names.putIfAbsent(metaKey, tznames); + if (isDefaultZone(meta, tzKey)) { + // Record the metazone names only from the default + // (001) zone, with short names filled from TZDB + tznames = Arrays.copyOf(tznames, tznames.length); + fillTZDBShortNames(tzKey, tznames); + names.put(metaKey, tznames); + } names.put(tzid, meta); if (tzLink != null && availableIds.contains(tzLink)) { names.put(tzLink, meta); @@ -867,6 +872,12 @@ public class CLDRConverter { .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); names.putAll(exCities); + // Explicit metazone offsets + if (id.equals("root")) { + explicitDstOffsets.forEach((k, v) -> + names.put(METAZONE_DSTOFFSET_PREFIX + k, v)); + } + // If there's no UTC entry at this point, add an empty one if (!names.isEmpty() && !names.containsKey("UTC")) { names.putIfAbsent(METAZONE_ID_PREFIX + META_EMPTY_ZONE_NAME, EMPTY_ZONE); @@ -1492,12 +1503,12 @@ public class CLDRConverter { * Fill the TZDB short names if there is no name provided by the CLDR */ private static void fillTZDBShortNames(String tzid, String[] names) { - var val = tzdbShortNamesMap.get(tzdbLinks.getOrDefault(tzid, tzid)); + var val = tzdbShortNamesMap.getOrDefault(tzid, tzdbShortNamesMap.get(getTZDBLink(tzid))); if (val != null) { var format = val.split(NBSP)[0]; var rule = val.split(NBSP)[1]; IntStream.of(1, 3, 5).forEach(i -> { - if (names[i] == null) { + if (names[i] == null || names[i].isEmpty()) { if (format.contains("%s")) { names[i] = switch (i) { case 1 -> format.formatted(tzdbSubstLetters.get(rule + NBSP + STD)); @@ -1519,6 +1530,21 @@ public class CLDRConverter { } } + private static boolean isDefaultZone(String meta, String tzid) { + String zone001 = handlerMetaZones.zidMap().get(meta); + var tzLink = getTZDBLink(tzid); + return canonicalTZMap.getOrDefault(tzid, tzid).equals(zone001) || + tzLink != null && canonicalTZMap.getOrDefault(tzLink, tzLink).equals(zone001); + } + + private static String getTZDBLink(String tzid) { + String tzLink = null; + for (var k = tzid; tzdbLinks.containsKey(k);) { + k = tzLink = tzdbLinks.get(k); + } + return tzLink; + } + /* * Convert TZDB offsets to JDK's offsets, eg, "-08" to "GMT-08:00". * If it cannot recognize the pattern, return the argument as is. diff --git a/make/jdk/src/classes/build/tools/cldrconverter/MetaZonesParseHandler.java b/make/jdk/src/classes/build/tools/cldrconverter/MetaZonesParseHandler.java index 2c3757b7a47..45de46d2476 100644 --- a/make/jdk/src/classes/build/tools/cldrconverter/MetaZonesParseHandler.java +++ b/make/jdk/src/classes/build/tools/cldrconverter/MetaZonesParseHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, 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 @@ -84,7 +84,15 @@ class MetaZonesParseHandler extends AbstractLDMLHandler { if (fromLDT.isBefore(now) && toLDT.isAfter(now)) { metazone = attributes.getValue("mzone"); + + // Explicit metazone DST offsets. Only the "dst" offset is needed, + // as "std" is used by default when it doesn't match. + String dstOffset = attributes.getValue("dstOffset"); + if (dstOffset != null) { + CLDRConverter.explicitDstOffsets.put(tzid, dstOffset); + } } + pushIgnoredContainer(qName); break; diff --git a/make/jdk/src/classes/build/tools/cldrconverter/ResourceBundleGenerator.java b/make/jdk/src/classes/build/tools/cldrconverter/ResourceBundleGenerator.java index 3953f38f653..8278bf6bcfa 100644 --- a/make/jdk/src/classes/build/tools/cldrconverter/ResourceBundleGenerator.java +++ b/make/jdk/src/classes/build/tools/cldrconverter/ResourceBundleGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, 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 @@ -198,7 +198,8 @@ class ResourceBundleGenerator implements BundleGenerator { } else if (value instanceof String) { String valStr = (String)value; if (type == BundleType.TIMEZONE && - !key.startsWith(CLDRConverter.EXEMPLAR_CITY_PREFIX) || + !(key.startsWith(CLDRConverter.EXEMPLAR_CITY_PREFIX) || + key.startsWith(CLDRConverter.METAZONE_DSTOFFSET_PREFIX)) || valStr.startsWith(META_VALUE_PREFIX)) { out.printf(" { \"%s\", %s },\n", key, CLDRConverter.saveConvert(valStr, useJava)); } else { diff --git a/make/jdk/src/classes/build/tools/taglet/JSpec.java b/make/jdk/src/classes/build/tools/taglet/JSpec.java index 7e1e0ca215e..196aaccb32b 100644 --- a/make/jdk/src/classes/build/tools/taglet/JSpec.java +++ b/make/jdk/src/classes/build/tools/taglet/JSpec.java @@ -25,13 +25,13 @@ package build.tools.taglet; +import java.net.URI; import java.util.EnumSet; import java.util.List; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; -import java.lang.reflect.Field; import javax.lang.model.element.Element; @@ -141,6 +141,11 @@ public class JSpec implements Taglet { @Override public String toString(List tags, Element elem) { + throw new UnsupportedOperationException(); + } + + // @Override - requires JDK-8373922 in build JDK + public String toString(List tags, Element elem, URI docRoot) { if (tags.isEmpty()) return ""; @@ -177,7 +182,7 @@ public class JSpec implements Taglet { String preview = m.group("preview"); // null if no preview feature String chapter = m.group("chapter"); String section = m.group("section"); - String rootParent = currentPath().replaceAll("[^/]+", ".."); + String rootParent = docRoot.resolve("..").toString(); String url = preview == null ? String.format("%1$s/specs/%2$s/%2$s-%3$s.html#%2$s-%3$s%4$s", @@ -230,23 +235,6 @@ public class JSpec implements Taglet { return sb.toString(); } - private static ThreadLocal CURRENT_PATH = null; - - private String currentPath() { - if (CURRENT_PATH == null) { - try { - Field f = Class.forName("jdk.javadoc.internal.doclets.formats.html.HtmlDocletWriter") - .getField("CURRENT_PATH"); - @SuppressWarnings("unchecked") - ThreadLocal tl = (ThreadLocal) f.get(null); - CURRENT_PATH = tl; - } catch (ReflectiveOperationException e) { - throw new RuntimeException("Cannot determine current path", e); - } - } - return CURRENT_PATH.get(); - } - private String expand(List trees) { return (new SimpleDocTreeVisitor() { public StringBuilder defaultAction(DocTree tree, StringBuilder sb) { diff --git a/make/jdk/src/classes/build/tools/taglet/SealedGraph.java b/make/jdk/src/classes/build/tools/taglet/SealedGraph.java index 3e93826c180..300999b77c0 100644 --- a/make/jdk/src/classes/build/tools/taglet/SealedGraph.java +++ b/make/jdk/src/classes/build/tools/taglet/SealedGraph.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, 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 @@ -32,7 +32,9 @@ import jdk.javadoc.doclet.Taglet; import javax.lang.model.element.*; import javax.lang.model.type.DeclaredType; +import javax.lang.model.util.Elements; import java.io.IOException; +import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; import java.util.*; @@ -78,6 +80,11 @@ public final class SealedGraph implements Taglet { @Override public String toString(List tags, Element element) { + throw new UnsupportedOperationException(); + } + + // @Override - requires JDK-8373922 in build JDK + public String toString(List tags, Element element, URI docRoot) { if (sealedDotOutputDir == null || sealedDotOutputDir.isEmpty()) { return ""; } @@ -85,9 +92,15 @@ public final class SealedGraph implements Taglet { return ""; } - ModuleElement module = docletEnvironment.getElementUtils().getModuleOf(element); + Elements util = docletEnvironment.getElementUtils(); + ModuleElement module = util.getModuleOf(element); + + // '.' in .DOT file name is converted to '/' in .SVG path, so we use '-' as separator for nested classes. + // module_package.subpackage.Outer-Inner.dot => module/package/subpackage/Outer-Inner-sealed-graph.svg Path dotFile = Path.of(sealedDotOutputDir, - module.getQualifiedName() + "_" + typeElement.getQualifiedName() + ".dot"); + module.getQualifiedName() + "_" + + util.getPackageOf(element).getQualifiedName() + "." + + packagelessCanonicalName(typeElement).replace(".", "-") + ".dot"); Set exports = module.getDirectives().stream() .filter(ModuleElement.ExportsDirective.class::isInstance) @@ -99,7 +112,7 @@ public final class SealedGraph implements Taglet { .map(Objects::toString) .collect(Collectors.toUnmodifiableSet()); - String dotContent = new Renderer().graph(typeElement, exports); + String dotContent = new Renderer().graph(typeElement, exports, docRoot); try { Files.writeString(dotFile, dotContent, WRITE, CREATE, TRUNCATE_EXISTING); @@ -107,8 +120,8 @@ public final class SealedGraph implements Taglet { throw new RuntimeException(e); } - String simpleTypeName = packagelessCanonicalName(typeElement).replace('.', '/'); - String imageFile = simpleTypeName + "-sealed-graph.svg"; + String simpleTypeName = packagelessCanonicalName(typeElement); + String imageFile = simpleTypeName.replace(".", "-") + "-sealed-graph.svg"; int thumbnailHeight = 100; // also appears in the stylesheet String hoverImage = "" + getImage(simpleTypeName, imageFile, -1, true) @@ -137,21 +150,26 @@ public final class SealedGraph implements Taglet { private final class Renderer { // Generates a graph in DOT format - String graph(TypeElement rootClass, Set exports) { - final State state = new State(rootClass); + String graph(TypeElement rootClass, Set exports, URI pathToRoot) { + if (!isInPublicApi(rootClass, exports)) { + // Alternatively we can return "" for the graph since there is no single root to render + throw new IllegalArgumentException("Root not in public API: " + rootClass.getQualifiedName()); + } + final State state = new State(pathToRoot); traverse(state, rootClass, exports); return state.render(); } static void traverse(State state, TypeElement node, Set exports) { + if (!isInPublicApi(node, exports)) { + throw new IllegalArgumentException("Bad request, not in public API: " + node.getQualifiedName()); + } state.addNode(node); if (!(node.getModifiers().contains(Modifier.SEALED) || node.getModifiers().contains(Modifier.FINAL))) { state.addNonSealedEdge(node); } else { for (TypeElement subNode : permittedSubclasses(node, exports)) { - if (isInPublicApi(node, exports) && isInPublicApi(subNode, exports)) { - state.addEdge(node, subNode); - } + state.addEdge(node, subNode); traverse(state, subNode, exports); } } @@ -163,7 +181,7 @@ public final class SealedGraph implements Taglet { private static final String TOOLTIP = "tooltip"; private static final String LINK = "href"; - private final TypeElement rootNode; + private final URI pathToRoot; private final StringBuilder builder; @@ -188,8 +206,8 @@ public final class SealedGraph implements Taglet { } } - public State(TypeElement rootNode) { - this.rootNode = rootNode; + public State(URI pathToRoot) { + this.pathToRoot = pathToRoot; nodeStyleMap = new LinkedHashMap<>(); builder = new StringBuilder() .append("digraph G {") @@ -212,24 +230,15 @@ public final class SealedGraph implements Taglet { var styles = nodeStyleMap.computeIfAbsent(id(node), n -> new LinkedHashMap<>()); styles.put(LABEL, new StyleItem.PlainString(node.getSimpleName().toString())); styles.put(TOOLTIP, new StyleItem.PlainString(node.getQualifiedName().toString())); - styles.put(LINK, new StyleItem.PlainString(relativeLink(node))); + styles.put(LINK, new StyleItem.PlainString(pathToRoot.resolve(relativeLink(node)).toString())); } - // A permitted class must be in the same package or in the same module. - // This implies the module is always the same. private String relativeLink(TypeElement node) { var util = SealedGraph.this.docletEnvironment.getElementUtils(); - var nodePackage = util.getPackageOf(node); - // Note: SVG files for nested types use the simple names of containing types as parent directories. - // We therefore need to convert all dots in the qualified name to "../" below. - var backNavigator = rootNode.getQualifiedName().toString().chars() - .filter(c -> c == '.') - .mapToObj(c -> "../") - .collect(joining()); - var forwardNavigator = nodePackage.getQualifiedName().toString() - .replace(".", "/"); + var path = util.getModuleOf(node).getQualifiedName().toString() + "/" + + util.getPackageOf(node).getQualifiedName().toString().replace(".", "/"); - return backNavigator + forwardNavigator + "/" + packagelessCanonicalName(node) + ".html"; + return path + "/" + packagelessCanonicalName(node) + ".html"; } public void addEdge(TypeElement node, TypeElement subNode) { @@ -281,25 +290,33 @@ public final class SealedGraph implements Taglet { private String quotedId(TypeElement node) { return "\"" + id(node) + "\""; } - - private String simpleName(String name) { - int lastDot = name.lastIndexOf('.'); - return lastDot < 0 - ? name - : name.substring(lastDot); - } - } private static List permittedSubclasses(TypeElement node, Set exports) { - return node.getPermittedSubclasses().stream() - .filter(DeclaredType.class::isInstance) - .map(DeclaredType.class::cast) - .map(DeclaredType::asElement) - .filter(TypeElement.class::isInstance) - .map(TypeElement.class::cast) - .filter(te -> isInPublicApi(te, exports)) - .toList(); + List dfsStack = new ArrayList().reversed(); // Faster operations to head + SequencedCollection result = new LinkedHashSet<>(); // Deduplicate diamond interface inheritance + // The starting node may be in the public API - still expand it + prependSubclasses(node, dfsStack); + + while (!dfsStack.isEmpty()) { + TypeElement now = dfsStack.removeFirst(); + if (isInPublicApi(now, exports)) { + result.addLast(now); + } else { + // Skip the non-exported classes in the hierarchy + prependSubclasses(now, dfsStack); + } + } + + return List.copyOf(result); + } + + private static void prependSubclasses(TypeElement node, List dfs) { + for (var e : node.getPermittedSubclasses().reversed()) { + if (e instanceof DeclaredType dt && dt.asElement() instanceof TypeElement te) { + dfs.addFirst(te); + } + } } private static boolean isInPublicApi(TypeElement typeElement, Set exports) { diff --git a/make/jdk/src/classes/build/tools/taglet/ToolGuide.java b/make/jdk/src/classes/build/tools/taglet/ToolGuide.java index 8db2aee3092..7ad4f6b9b9f 100644 --- a/make/jdk/src/classes/build/tools/taglet/ToolGuide.java +++ b/make/jdk/src/classes/build/tools/taglet/ToolGuide.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, 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 @@ -25,13 +25,13 @@ package build.tools.taglet; +import java.net.URI; import java.util.EnumSet; import java.util.List; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; -import java.lang.reflect.Field; import javax.lang.model.element.Element; @@ -91,6 +91,11 @@ public class ToolGuide implements Taglet { @Override public String toString(List tags, Element elem) { + throw new UnsupportedOperationException(); + } + + // @Override - requires JDK-8373922 in build JDK + public String toString(List tags, Element elem, URI docRoot) { if (tags.isEmpty()) return ""; @@ -118,7 +123,7 @@ public class ToolGuide implements Taglet { if (label.isEmpty()) { label = name; } - String rootParent = currentPath().replaceAll("[^/]+", ".."); + String rootParent = docRoot.resolve("..").toString(); String url = String.format("%s/%s/%s.html", rootParent, BASE_URL, name); @@ -141,22 +146,4 @@ public class ToolGuide implements Taglet { return sb.toString(); } - - private static ThreadLocal CURRENT_PATH = null; - - private String currentPath() { - if (CURRENT_PATH == null) { - try { - Field f = Class.forName("jdk.javadoc.internal.doclets.formats.html.HtmlDocletWriter") - .getField("CURRENT_PATH"); - @SuppressWarnings("unchecked") - ThreadLocal tl = (ThreadLocal) f.get(null); - CURRENT_PATH = tl; - } catch (ReflectiveOperationException e) { - throw new RuntimeException("Cannot determine current path", e); - } - } - return CURRENT_PATH.get(); - } - } diff --git a/make/langtools/tools/previewfeature/SetupPreviewFeature.java b/make/langtools/tools/previewfeature/SetupPreviewFeature.java index 2d5207f0e17..5f9b00edc6d 100644 --- a/make/langtools/tools/previewfeature/SetupPreviewFeature.java +++ b/make/langtools/tools/previewfeature/SetupPreviewFeature.java @@ -30,6 +30,7 @@ import java.io.StringWriter; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.StandardCopyOption; import java.util.HashSet; import java.util.Set; import java.util.stream.Collectors; @@ -76,7 +77,7 @@ public class SetupPreviewFeature { var target = Path.of(args[1]); Files.createDirectories(target.getParent()); if (constantsToAdd.isEmpty()) { - Files.copy(source, target); + Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING); } else { String sourceCode = Files.readString(source); try (var out = Files.newBufferedWriter(target)) { diff --git a/make/modules/java.desktop/lib/ClientLibraries.gmk b/make/modules/java.desktop/lib/ClientLibraries.gmk index b76cb8dc4e3..2326505d11c 100644 --- a/make/modules/java.desktop/lib/ClientLibraries.gmk +++ b/make/modules/java.desktop/lib/ClientLibraries.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2026, 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 @@ -257,6 +257,7 @@ ifeq ($(ENABLE_HEADLESS_ONLY), false) DISABLED_WARNINGS_microsoft_dgif_lib.c := 4018 4267, \ DISABLED_WARNINGS_microsoft_splashscreen_impl.c := 4018 4267 4244, \ DISABLED_WARNINGS_microsoft_splashscreen_png.c := 4267, \ + DISABLED_WARNINGS_microsoft_pngread.c := 4146, \ DISABLED_WARNINGS_microsoft_splashscreen_sys.c := 4267 4244, \ LDFLAGS := $(ICONV_LDFLAGS), \ LDFLAGS_windows := -delayload:user32.dll, \ @@ -338,11 +339,8 @@ else # noexcept-type required for GCC 7 builds. Not required for GCC 8+. # expansion-to-defined required for GCC 9 builds. Not required for GCC 10+. # maybe-uninitialized required for GCC 8 builds. Not required for GCC 9+. - # calloc-transposed-args required for GCC 14 builds. (fixed upstream in - # Harfbuzz 032c931e1c0cfb20f18e5acb8ba005775242bd92) HARFBUZZ_DISABLED_WARNINGS_CXX_gcc := class-memaccess noexcept-type \ - expansion-to-defined dangling-reference maybe-uninitialized \ - calloc-transposed-args + expansion-to-defined dangling-reference maybe-uninitialized HARFBUZZ_DISABLED_WARNINGS_clang := missing-field-initializers \ range-loop-analysis unused-variable HARFBUZZ_DISABLED_WARNINGS_microsoft := 4267 4244 @@ -397,6 +395,8 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBFONTMANAGER, \ AccelGlyphCache.c, \ CFLAGS := $(LIBFONTMANAGER_CFLAGS), \ CXXFLAGS := $(LIBFONTMANAGER_CFLAGS), \ + CXXFLAGS_gcc := -fno-rtti -fno-exceptions, \ + CXXFLAGS_clang := -fno-rtti -fno-exceptions, \ OPTIMIZATION := HIGHEST, \ CFLAGS_windows = -DCC_NOEX, \ EXTRA_HEADER_DIRS := $(LIBFONTMANAGER_EXTRA_HEADER_DIRS), \ diff --git a/make/modules/jdk.hotspot.agent/Lib.gmk b/make/modules/jdk.hotspot.agent/Lib.gmk index ed8de631dc3..da02e0dab39 100644 --- a/make/modules/jdk.hotspot.agent/Lib.gmk +++ b/make/modules/jdk.hotspot.agent/Lib.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2015, 2026, 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 @@ -55,6 +55,12 @@ else LIBSAPROC_LINK_TYPE := C endif +# DWARF related sources would be included on supported platforms only. +LIBSAPROC_EXCLUDE_FILES := +ifneq ($(call And, $(call isTargetOs, linux) $(call isTargetCpu, x86_64 aarch64)), true) + LIBSAPROC_EXCLUDE_FILES := DwarfParser.cpp dwarf.cpp +endif + $(eval $(call SetupJdkLibrary, BUILD_LIBSAPROC, \ NAME := saproc, \ LINK_TYPE := $(LIBSAPROC_LINK_TYPE), \ @@ -70,6 +76,7 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBSAPROC, \ CFLAGS := $(LIBSAPROC_CFLAGS), \ CXXFLAGS := $(LIBSAPROC_CFLAGS) $(LIBSAPROC_CXXFLAGS), \ EXTRA_SRC := $(LIBSAPROC_EXTRA_SRC), \ + EXCLUDE_FILES := $(LIBSAPROC_EXCLUDE_FILES), \ JDK_LIBS := java.base:libjava, \ LIBS_linux := $(LIBDL), \ LIBS_macosx := \ diff --git a/make/scripts/fixpath.sh b/make/scripts/fixpath.sh index 6a524df4c68..78690f1f2cc 100644 --- a/make/scripts/fixpath.sh +++ b/make/scripts/fixpath.sh @@ -88,7 +88,10 @@ function setup() { fi if [[ -z ${CMD+x} ]]; then - CMD="$DRIVEPREFIX/c/windows/system32/cmd.exe" + CMD="$(type -p cmd.exe 2>/dev/null)" + if [[ -z "$CMD" ]]; then + CMD="$DRIVEPREFIX/c/windows/system32/cmd.exe" + fi fi if [[ -z ${WINTEMP+x} ]]; then diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index b79030f07e7..53fa4e3066c 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -1182,12 +1182,12 @@ class CallStubImpl { public: // Size of call trampoline stub. static uint size_call_trampoline() { - return 0; // no call trampolines on this platform + return MacroAssembler::max_trampoline_stub_size(); } // number of relocations needed by a call trampoline stub static uint reloc_call_trampoline() { - return 0; // no call trampolines on this platform + return 5; // metadata; call dest; trampoline address; trampoline destination; trampoline_owner_metadata } }; @@ -2233,15 +2233,9 @@ uint BoxLockNode::size(PhaseRegAlloc *ra_) const { void MachUEPNode::format(PhaseRegAlloc* ra_, outputStream* st) const { st->print_cr("# MachUEPNode"); - if (UseCompressedClassPointers) { - st->print_cr("\tldrw rscratch1, [j_rarg0 + oopDesc::klass_offset_in_bytes()]\t# compressed klass"); - st->print_cr("\tldrw r10, [rscratch2 + CompiledICData::speculated_klass_offset()]\t# compressed klass"); - st->print_cr("\tcmpw rscratch1, r10"); - } else { - st->print_cr("\tldr rscratch1, [j_rarg0 + oopDesc::klass_offset_in_bytes()]\t# compressed klass"); - st->print_cr("\tldr r10, [rscratch2 + CompiledICData::speculated_klass_offset()]\t# compressed klass"); - st->print_cr("\tcmp rscratch1, r10"); - } + st->print_cr("\tldrw rscratch1, [j_rarg0 + oopDesc::klass_offset_in_bytes()]\t# compressed klass"); + st->print_cr("\tldrw r10, [rscratch2 + CompiledICData::speculated_klass_offset()]\t# compressed klass"); + st->print_cr("\tcmpw rscratch1, r10"); st->print_cr("\tbne, SharedRuntime::_ic_miss_stub"); } #endif diff --git a/src/hotspot/cpu/aarch64/aarch64_vector.ad b/src/hotspot/cpu/aarch64/aarch64_vector.ad index 19f03d97a72..4c854913e63 100644 --- a/src/hotspot/cpu/aarch64/aarch64_vector.ad +++ b/src/hotspot/cpu/aarch64/aarch64_vector.ad @@ -1,6 +1,6 @@ // // Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. -// Copyright (c) 2020, 2025, Arm Limited. All rights reserved. +// Copyright (c) 2020, 2026, Arm Limited. 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 @@ -247,10 +247,39 @@ source %{ case Op_MinVHF: case Op_MaxVHF: case Op_SqrtVHF: + if (UseSVE == 0 && !is_feat_fp16_supported()) { + return false; + } + break; + // At the time of writing this, the Vector API has no half-float (FP16) species. + // Consequently, AddReductionVHF and MulReductionVHF are only produced by the + // auto-vectorizer, which requires strictly ordered semantics for FP reductions. + // + // There is no direct Neon instruction that performs strictly ordered floating + // point add reduction. Hence, on Neon only machines, the add reduction operation + // is implemented as a scalarized sequence using half-precision scalar instruction + // FADD which requires FEAT_FP16 and ASIMDHP to be available on the target. + // On SVE machines (UseSVE > 0) however, there is a direct instruction (FADDA) which + // implements strictly ordered floating point add reduction which does not require + // the FEAT_FP16 and ASIMDHP checks as SVE supports half-precision floats by default. + case Op_AddReductionVHF: // FEAT_FP16 is enabled if both "fphp" and "asimdhp" features are supported. // Only the Neon instructions need this check. SVE supports half-precision floats // by default. - if (UseSVE == 0 && !is_feat_fp16_supported()) { + if (length_in_bytes < 8 || (UseSVE == 0 && !is_feat_fp16_supported())) { + return false; + } + break; + case Op_MulReductionVHF: + // There are no direct Neon/SVE instructions that perform strictly ordered + // floating point multiply reduction. + // For vector length ≤ 16 bytes, the reduction is implemented as a scalarized + // sequence using half-precision scalar instruction FMUL. This path requires + // FEAT_FP16 and ASIMDHP to be available on the target. + // For vector length > 16 bytes, this operation is disabled because there is no + // direct SVE instruction that performs a strictly ordered FP16 multiply + // reduction. + if (length_in_bytes < 8 || length_in_bytes > 16 || !is_feat_fp16_supported()) { return false; } break; @@ -300,6 +329,7 @@ source %{ case Op_VectorRearrange: case Op_MulReductionVD: case Op_MulReductionVF: + case Op_MulReductionVHF: case Op_MulReductionVI: case Op_MulReductionVL: case Op_CompressBitsV: @@ -364,6 +394,7 @@ source %{ case Op_VectorMaskCmp: case Op_LoadVectorGather: case Op_StoreVectorScatter: + case Op_AddReductionVHF: case Op_AddReductionVF: case Op_AddReductionVD: case Op_AndReductionV: @@ -597,13 +628,9 @@ instruct vloadcon(vReg dst, immI0 src) %{ BasicType bt = Matcher::vector_element_basic_type(this); if (UseSVE == 0) { uint length_in_bytes = Matcher::vector_length_in_bytes(this); + int entry_idx = __ vector_iota_entry_index(bt); assert(length_in_bytes <= 16, "must be"); - // The iota indices are ordered by type B/S/I/L/F/D, and the offset between two types is 16. - int offset = exact_log2(type2aelembytes(bt)) << 4; - if (is_floating_point_type(bt)) { - offset += 32; - } - __ lea(rscratch1, ExternalAddress(StubRoutines::aarch64::vector_iota_indices() + offset)); + __ lea(rscratch1, ExternalAddress(StubRoutines::aarch64::vector_iota_indices(entry_idx))); if (length_in_bytes == 16) { __ ldrq($dst$$FloatRegister, rscratch1); } else { @@ -3406,6 +3433,44 @@ instruct reduce_non_strict_order_add4F_neon(vRegF dst, vRegF fsrc, vReg vsrc, vR ins_pipe(pipe_slow); %} +// Add Reduction for Half floats (FP16). +// Neon does not provide direct instructions for strictly ordered floating-point add reductions. +// On Neon-only targets (UseSVE = 0), this operation is implemented as a sequence of scalar additions: +// values equal to the vector width are loaded into a vector register, each lane is extracted, +// and its value is accumulated into the running sum, producing a final scalar result. +instruct reduce_addHF_neon(vRegF dst, vRegF fsrc, vReg vsrc, vReg tmp) %{ + predicate(UseSVE == 0); + match(Set dst (AddReductionVHF fsrc vsrc)); + effect(TEMP_DEF dst, TEMP tmp); + format %{ "reduce_addHF $dst, $fsrc, $vsrc\t# 4HF/8HF. KILL $tmp" %} + ins_encode %{ + uint length_in_bytes = Matcher::vector_length_in_bytes(this, $vsrc); + __ neon_reduce_add_fp16($dst$$FloatRegister, $fsrc$$FloatRegister, + $vsrc$$FloatRegister, length_in_bytes, $tmp$$FloatRegister); + %} + ins_pipe(pipe_slow); +%} + +// This rule calculates the reduction result in strict order. Two cases will +// reach here: +// 1. Non strictly-ordered AddReductionVHF when vector size > 128-bits. For example - +// AddReductionVHF generated by Vector API. For vector size > 128-bits, it is more +// beneficial performance-wise to generate direct SVE instruction even if it is +// strictly ordered. +// 2. Strictly-ordered AddReductionVHF. For example - AddReductionVHF generated by +// auto-vectorization on SVE machine. +instruct reduce_addHF_sve(vRegF dst_src1, vReg src2) %{ + predicate(UseSVE > 0); + match(Set dst_src1 (AddReductionVHF dst_src1 src2)); + format %{ "reduce_addHF_sve $dst_src1, $dst_src1, $src2" %} + ins_encode %{ + uint length_in_bytes = Matcher::vector_length_in_bytes(this, $src2); + assert(length_in_bytes == MaxVectorSize, "invalid vector length"); + __ sve_fadda($dst_src1$$FloatRegister, __ H, ptrue, $src2$$FloatRegister); + %} + ins_pipe(pipe_slow); +%} + // This rule calculates the reduction result in strict order. Two cases will // reach here: // 1. Non strictly-ordered AddReductionVF when vector size > 128-bits. For example - @@ -3496,12 +3561,14 @@ instruct reduce_addL_masked(iRegLNoSp dst, iRegL isrc, vReg vsrc, pRegGov pg, vR ins_pipe(pipe_slow); %} -instruct reduce_addF_masked(vRegF dst_src1, vReg src2, pRegGov pg) %{ +instruct reduce_addFHF_masked(vRegF dst_src1, vReg src2, pRegGov pg) %{ predicate(UseSVE > 0); + match(Set dst_src1 (AddReductionVHF (Binary dst_src1 src2) pg)); match(Set dst_src1 (AddReductionVF (Binary dst_src1 src2) pg)); - format %{ "reduce_addF_masked $dst_src1, $pg, $dst_src1, $src2" %} + format %{ "reduce_addFHF_masked $dst_src1, $pg, $dst_src1, $src2" %} ins_encode %{ - __ sve_fadda($dst_src1$$FloatRegister, __ S, + BasicType bt = Matcher::vector_element_basic_type(this, $src2); + __ sve_fadda($dst_src1$$FloatRegister, __ elemType_to_regVariant(bt), $pg$$PRegister, $src2$$FloatRegister); %} ins_pipe(pipe_slow); @@ -3549,14 +3616,17 @@ instruct reduce_mulL(iRegLNoSp dst, iRegL isrc, vReg vsrc) %{ ins_pipe(pipe_slow); %} -instruct reduce_mulF(vRegF dst, vRegF fsrc, vReg vsrc, vReg tmp) %{ + +instruct reduce_mulFHF(vRegF dst, vRegF fsrc, vReg vsrc, vReg tmp) %{ predicate(Matcher::vector_length_in_bytes(n->in(2)) <= 16); + match(Set dst (MulReductionVHF fsrc vsrc)); match(Set dst (MulReductionVF fsrc vsrc)); effect(TEMP_DEF dst, TEMP tmp); - format %{ "reduce_mulF $dst, $fsrc, $vsrc\t# 2F/4F. KILL $tmp" %} + format %{ "reduce_mulFHF $dst, $fsrc, $vsrc\t# 2F/4F/4HF/8HF. KILL $tmp" %} ins_encode %{ uint length_in_bytes = Matcher::vector_length_in_bytes(this, $vsrc); - __ neon_reduce_mul_fp($dst$$FloatRegister, T_FLOAT, $fsrc$$FloatRegister, + BasicType bt = Matcher::vector_element_basic_type(this, $vsrc); + __ neon_reduce_mul_fp($dst$$FloatRegister, bt, $fsrc$$FloatRegister, $vsrc$$FloatRegister, length_in_bytes, $tmp$$FloatRegister); %} ins_pipe(pipe_slow); diff --git a/src/hotspot/cpu/aarch64/aarch64_vector_ad.m4 b/src/hotspot/cpu/aarch64/aarch64_vector_ad.m4 index 48bffb3cf35..58ed234194a 100644 --- a/src/hotspot/cpu/aarch64/aarch64_vector_ad.m4 +++ b/src/hotspot/cpu/aarch64/aarch64_vector_ad.m4 @@ -1,6 +1,6 @@ // // Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. -// Copyright (c) 2020, 2025, Arm Limited. All rights reserved. +// Copyright (c) 2020, 2026, Arm Limited. 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 @@ -237,10 +237,39 @@ source %{ case Op_MinVHF: case Op_MaxVHF: case Op_SqrtVHF: + if (UseSVE == 0 && !is_feat_fp16_supported()) { + return false; + } + break; + // At the time of writing this, the Vector API has no half-float (FP16) species. + // Consequently, AddReductionVHF and MulReductionVHF are only produced by the + // auto-vectorizer, which requires strictly ordered semantics for FP reductions. + // + // There is no direct Neon instruction that performs strictly ordered floating + // point add reduction. Hence, on Neon only machines, the add reduction operation + // is implemented as a scalarized sequence using half-precision scalar instruction + // FADD which requires FEAT_FP16 and ASIMDHP to be available on the target. + // On SVE machines (UseSVE > 0) however, there is a direct instruction (FADDA) which + // implements strictly ordered floating point add reduction which does not require + // the FEAT_FP16 and ASIMDHP checks as SVE supports half-precision floats by default. + case Op_AddReductionVHF: // FEAT_FP16 is enabled if both "fphp" and "asimdhp" features are supported. // Only the Neon instructions need this check. SVE supports half-precision floats // by default. - if (UseSVE == 0 && !is_feat_fp16_supported()) { + if (length_in_bytes < 8 || (UseSVE == 0 && !is_feat_fp16_supported())) { + return false; + } + break; + case Op_MulReductionVHF: + // There are no direct Neon/SVE instructions that perform strictly ordered + // floating point multiply reduction. + // For vector length ≤ 16 bytes, the reduction is implemented as a scalarized + // sequence using half-precision scalar instruction FMUL. This path requires + // FEAT_FP16 and ASIMDHP to be available on the target. + // For vector length > 16 bytes, this operation is disabled because there is no + // direct SVE instruction that performs a strictly ordered FP16 multiply + // reduction. + if (length_in_bytes < 8 || length_in_bytes > 16 || !is_feat_fp16_supported()) { return false; } break; @@ -290,6 +319,7 @@ source %{ case Op_VectorRearrange: case Op_MulReductionVD: case Op_MulReductionVF: + case Op_MulReductionVHF: case Op_MulReductionVI: case Op_MulReductionVL: case Op_CompressBitsV: @@ -354,6 +384,7 @@ source %{ case Op_VectorMaskCmp: case Op_LoadVectorGather: case Op_StoreVectorScatter: + case Op_AddReductionVHF: case Op_AddReductionVF: case Op_AddReductionVD: case Op_AndReductionV: @@ -2063,6 +2094,25 @@ instruct reduce_non_strict_order_add4F_neon(vRegF dst, vRegF fsrc, vReg vsrc, vR ins_pipe(pipe_slow); %} dnl + +// Add Reduction for Half floats (FP16). +// Neon does not provide direct instructions for strictly ordered floating-point add reductions. +// On Neon-only targets (UseSVE = 0), this operation is implemented as a sequence of scalar additions: +// values equal to the vector width are loaded into a vector register, each lane is extracted, +// and its value is accumulated into the running sum, producing a final scalar result. +instruct reduce_addHF_neon(vRegF dst, vRegF fsrc, vReg vsrc, vReg tmp) %{ + predicate(UseSVE == 0); + match(Set dst (AddReductionVHF fsrc vsrc)); + effect(TEMP_DEF dst, TEMP tmp); + format %{ "reduce_addHF $dst, $fsrc, $vsrc\t# 4HF/8HF. KILL $tmp" %} + ins_encode %{ + uint length_in_bytes = Matcher::vector_length_in_bytes(this, $vsrc); + __ neon_reduce_add_fp16($dst$$FloatRegister, $fsrc$$FloatRegister, + $vsrc$$FloatRegister, length_in_bytes, $tmp$$FloatRegister); + %} + ins_pipe(pipe_slow); +%} +dnl dnl REDUCE_ADD_FP_SVE($1, $2 ) dnl REDUCE_ADD_FP_SVE(type, size) define(`REDUCE_ADD_FP_SVE', ` @@ -2074,21 +2124,26 @@ define(`REDUCE_ADD_FP_SVE', ` // strictly ordered. // 2. Strictly-ordered AddReductionV$1. For example - AddReductionV$1 generated by // auto-vectorization on SVE machine. -instruct reduce_add$1_sve(vReg$1 dst_src1, vReg src2) %{ - predicate(!VM_Version::use_neon_for_vector(Matcher::vector_length_in_bytes(n->in(2))) || - n->as_Reduction()->requires_strict_order()); +instruct reduce_add$1_sve(vReg`'ifelse($1, HF, F, $1) dst_src1, vReg src2) %{ + ifelse($1, HF, + `predicate(UseSVE > 0);', + `predicate(!VM_Version::use_neon_for_vector(Matcher::vector_length_in_bytes(n->in(2))) || + n->as_Reduction()->requires_strict_order());') match(Set dst_src1 (AddReductionV$1 dst_src1 src2)); format %{ "reduce_add$1_sve $dst_src1, $dst_src1, $src2" %} ins_encode %{ - assert(UseSVE > 0, "must be sve"); - uint length_in_bytes = Matcher::vector_length_in_bytes(this, $src2); + ifelse($1, HF, `', + `assert(UseSVE > 0, "must be sve"); + ')dnl +uint length_in_bytes = Matcher::vector_length_in_bytes(this, $src2); assert(length_in_bytes == MaxVectorSize, "invalid vector length"); __ sve_fadda($dst_src1$$FloatRegister, __ $2, ptrue, $src2$$FloatRegister); %} ins_pipe(pipe_slow); %}')dnl dnl -REDUCE_ADD_FP_SVE(F, S) +REDUCE_ADD_FP_SVE(HF, H) +REDUCE_ADD_FP_SVE(F, S) // reduction addD @@ -2129,21 +2184,30 @@ dnl dnl REDUCE_ADD_FP_PREDICATE($1, $2 ) dnl REDUCE_ADD_FP_PREDICATE(insn_name, op_name) define(`REDUCE_ADD_FP_PREDICATE', ` -instruct reduce_add$1_masked(vReg$1 dst_src1, vReg src2, pRegGov pg) %{ +instruct reduce_add$1_masked(vReg$2 dst_src1, vReg src2, pRegGov pg) %{ predicate(UseSVE > 0); - match(Set dst_src1 (AddReductionV$1 (Binary dst_src1 src2) pg)); + ifelse($2, F, + `match(Set dst_src1 (AddReductionVHF (Binary dst_src1 src2) pg)); + match(Set dst_src1 (AddReductionV$2 (Binary dst_src1 src2) pg));', + `match(Set dst_src1 (AddReductionV$2 (Binary dst_src1 src2) pg));') format %{ "reduce_add$1_masked $dst_src1, $pg, $dst_src1, $src2" %} ins_encode %{ - __ sve_fadda($dst_src1$$FloatRegister, __ $2, - $pg$$PRegister, $src2$$FloatRegister); + ifelse($2, F, + `BasicType bt = Matcher::vector_element_basic_type(this, $src2); + ',)dnl +ifelse($2, F, + `__ sve_fadda($dst_src1$$FloatRegister, __ elemType_to_regVariant(bt), + $pg$$PRegister, $src2$$FloatRegister);', + `__ sve_fadda($dst_src1$$FloatRegister, __ $2, + $pg$$PRegister, $src2$$FloatRegister);') %} ins_pipe(pipe_slow); %}')dnl dnl REDUCE_ADD_INT_PREDICATE(I, iRegIorL2I) REDUCE_ADD_INT_PREDICATE(L, iRegL) -REDUCE_ADD_FP_PREDICATE(F, S) -REDUCE_ADD_FP_PREDICATE(D, D) +REDUCE_ADD_FP_PREDICATE(FHF, F) +REDUCE_ADD_FP_PREDICATE(D, D) // ------------------------------ Vector reduction mul ------------------------- @@ -2176,30 +2240,37 @@ instruct reduce_mulL(iRegLNoSp dst, iRegL isrc, vReg vsrc) %{ ins_pipe(pipe_slow); %} -instruct reduce_mulF(vRegF dst, vRegF fsrc, vReg vsrc, vReg tmp) %{ - predicate(Matcher::vector_length_in_bytes(n->in(2)) <= 16); - match(Set dst (MulReductionVF fsrc vsrc)); +dnl REDUCE_MUL_FP($1, $2 ) +dnl REDUCE_MUL_FP(insn_name, op_name) +define(`REDUCE_MUL_FP', ` +instruct reduce_mul$1(vReg$2 dst, vReg$2 ifelse($2, F, fsrc, dsrc), vReg vsrc, vReg tmp) %{ + predicate(Matcher::vector_length_in_bytes(n->in(2)) ifelse($2, F, <=, ==) 16); + ifelse($2, F, + `match(Set dst (MulReductionVHF fsrc vsrc)); + match(Set dst (MulReductionV$2 fsrc vsrc));', + `match(Set dst (MulReductionV$2 dsrc vsrc));') effect(TEMP_DEF dst, TEMP tmp); - format %{ "reduce_mulF $dst, $fsrc, $vsrc\t# 2F/4F. KILL $tmp" %} + ifelse($2, F, + `format %{ "reduce_mul$1 $dst, $fsrc, $vsrc\t# 2F/4F/4HF/8HF. KILL $tmp" %}', + `format %{ "reduce_mul$1 $dst, $dsrc, $vsrc\t# 2D. KILL $tmp" %}') ins_encode %{ - uint length_in_bytes = Matcher::vector_length_in_bytes(this, $vsrc); - __ neon_reduce_mul_fp($dst$$FloatRegister, T_FLOAT, $fsrc$$FloatRegister, - $vsrc$$FloatRegister, length_in_bytes, $tmp$$FloatRegister); + ifelse($2, F, + `uint length_in_bytes = Matcher::vector_length_in_bytes(this, $vsrc); + ',)dnl +ifelse($2, F, + `BasicType bt = Matcher::vector_element_basic_type(this, $vsrc); + ',)dnl +ifelse($2, F, + `__ neon_reduce_mul_fp($dst$$FloatRegister, bt, $fsrc$$FloatRegister, + $vsrc$$FloatRegister, length_in_bytes, $tmp$$FloatRegister);', + `__ neon_reduce_mul_fp($dst$$FloatRegister, T_DOUBLE, $dsrc$$FloatRegister, + $vsrc$$FloatRegister, 16, $tmp$$FloatRegister);') %} ins_pipe(pipe_slow); -%} - -instruct reduce_mulD(vRegD dst, vRegD dsrc, vReg vsrc, vReg tmp) %{ - predicate(Matcher::vector_length_in_bytes(n->in(2)) == 16); - match(Set dst (MulReductionVD dsrc vsrc)); - effect(TEMP_DEF dst, TEMP tmp); - format %{ "reduce_mulD $dst, $dsrc, $vsrc\t# 2D. KILL $tmp" %} - ins_encode %{ - __ neon_reduce_mul_fp($dst$$FloatRegister, T_DOUBLE, $dsrc$$FloatRegister, - $vsrc$$FloatRegister, 16, $tmp$$FloatRegister); - %} - ins_pipe(pipe_slow); -%} +%}')dnl +dnl +REDUCE_MUL_FP(FHF, F) +REDUCE_MUL_FP(D, D) dnl dnl REDUCE_BITWISE_OP_NEON($1, $2 $3 $4 ) diff --git a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp index 67cf77989d2..c8d5ee2eaeb 100644 --- a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp @@ -1,6 +1,7 @@ /* * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2024, Red Hat Inc. All rights reserved. + * Copyright 2026 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 @@ -1000,30 +1001,6 @@ public: f(0b0101010, 31, 25), f(0, 24), sf(offset, 23, 5), f(0, 4), f(cond, 3, 0); } -#define INSN(NAME, cond) \ - void NAME(address dest) { \ - br(cond, dest); \ - } - - INSN(beq, EQ); - INSN(bne, NE); - INSN(bhs, HS); - INSN(bcs, CS); - INSN(blo, LO); - INSN(bcc, CC); - INSN(bmi, MI); - INSN(bpl, PL); - INSN(bvs, VS); - INSN(bvc, VC); - INSN(bhi, HI); - INSN(bls, LS); - INSN(bge, GE); - INSN(blt, LT); - INSN(bgt, GT); - INSN(ble, LE); - INSN(bal, AL); - INSN(bnv, NV); - void br(Condition cc, Label &L); #undef INSN @@ -1095,6 +1072,10 @@ public: #undef INSN + void wfet(Register rt) { + system(0b00, 0b011, 0b0001, 0b0000, 0b000, rt); + } + // we only provide mrs and msr for the special purpose system // registers where op1 (instr[20:19]) == 11 // n.b msr has L (instr[21]) == 0 mrs has L == 1 @@ -1274,6 +1255,13 @@ public: sz, 0b000, ordered); } + void load_store_volatile(Register data, BasicType type, Register addr, + bool is_load) { + load_store_exclusive(dummy_reg, data, dummy_reg, addr, + (Assembler::operand_size)exact_log2(type2aelembytes(type)), + is_load ? 0b110 : 0b100, /* ordered = */ true); + } + #define INSN4(NAME, sz, op, o0) /* Four registers */ \ void NAME(Register Rs, Register Rt1, Register Rt2, Register Rn) { \ guarantee(Rs != Rn && Rs != Rt1 && Rs != Rt2, "unpredictable instruction"); \ diff --git a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp index 30048a2079d..4eb4e3d5ac7 100644 --- a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp @@ -1,6 +1,7 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2020, Red Hat Inc. All rights reserved. + * Copyright 2026 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 @@ -42,6 +43,7 @@ #include "runtime/frame.inline.hpp" #include "runtime/sharedRuntime.hpp" #include "runtime/stubRoutines.hpp" +#include "runtime/threadIdentifier.hpp" #include "utilities/powerOfTwo.hpp" #include "vmreg_aarch64.inline.hpp" @@ -59,22 +61,6 @@ const Register SHIFT_count = r0; // where count for shift operations must be #define __ _masm-> -static void select_different_registers(Register preserve, - Register extra, - Register &tmp1, - Register &tmp2) { - if (tmp1 == preserve) { - assert_different_registers(tmp1, tmp2, extra); - tmp1 = extra; - } else if (tmp2 == preserve) { - assert_different_registers(tmp1, tmp2, extra); - tmp2 = extra; - } - assert_different_registers(preserve, tmp1, tmp2); -} - - - static void select_different_registers(Register preserve, Register extra, Register &tmp1, @@ -536,6 +522,10 @@ void LIR_Assembler::const2reg(LIR_Opr src, LIR_Opr dest, LIR_PatchCode patch_cod #if INCLUDE_CDS if (AOTCodeCache::is_on_for_dump()) { address b = c->as_pointer(); + if (b == (address)ThreadIdentifier::unsafe_offset()) { + __ lea(dest->as_register_lo(), ExternalAddress(b)); + break; + } if (AOTRuntimeConstants::contains(b)) { __ load_aotrc_address(dest->as_register_lo(), b); break; @@ -922,8 +912,15 @@ void LIR_Assembler::stack2stack(LIR_Opr src, LIR_Opr dest, BasicType type) { reg2stack(temp, dest, dest->type()); } +void LIR_Assembler::mem2reg(LIR_Opr src, LIR_Opr dest, BasicType type, + LIR_PatchCode patch_code, CodeEmitInfo* info, + bool wide) { + mem2reg(src, dest, type, patch_code, info, wide, false); +} -void LIR_Assembler::mem2reg(LIR_Opr src, LIR_Opr dest, BasicType type, LIR_PatchCode patch_code, CodeEmitInfo* info, bool wide) { +void LIR_Assembler::mem2reg(LIR_Opr src, LIR_Opr dest, BasicType type, + LIR_PatchCode patch_code, CodeEmitInfo* info, + bool wide, bool is_volatile) { LIR_Address* addr = src->as_address_ptr(); LIR_Address* from_addr = src->as_address_ptr(); @@ -936,10 +933,27 @@ void LIR_Assembler::mem2reg(LIR_Opr src, LIR_Opr dest, BasicType type, LIR_Patch return; } + if (is_volatile) { + load_volatile(from_addr, dest, type, info); + } else { + load_unordered(from_addr, dest, type, wide, info); + } + + if (is_reference_type(type)) { + if (UseCompressedOops && !wide) { + __ decode_heap_oop(dest->as_register()); + } + + __ verify_oop(dest->as_register()); + } +} + +void LIR_Assembler::load_unordered(LIR_Address *from_addr, LIR_Opr dest, + BasicType type, bool wide, CodeEmitInfo* info) { if (info != nullptr) { add_debug_info_for_null_check_here(info); } - int null_check_here = code_offset(); + switch (type) { case T_FLOAT: { __ ldrs(dest->as_float_reg(), as_Address(from_addr)); @@ -997,16 +1011,44 @@ void LIR_Assembler::mem2reg(LIR_Opr src, LIR_Opr dest, BasicType type, LIR_Patch default: ShouldNotReachHere(); } - - if (is_reference_type(type)) { - if (UseCompressedOops && !wide) { - __ decode_heap_oop(dest->as_register()); - } - - __ verify_oop(dest->as_register()); - } } +void LIR_Assembler::load_volatile(LIR_Address *from_addr, LIR_Opr dest, + BasicType type, CodeEmitInfo* info) { + __ lea(rscratch1, as_Address(from_addr)); + + Register dest_reg = rscratch2; + if (!is_floating_point_type(type)) { + dest_reg = (dest->is_single_cpu() + ? dest->as_register() : dest->as_register_lo()); + } + + if (info != nullptr) { + add_debug_info_for_null_check_here(info); + } + + // Uses LDAR to ensure memory ordering. + __ load_store_volatile(dest_reg, type, rscratch1, /*is_load*/true); + + switch (type) { + // LDAR is unsigned so need to sign-extend for byte and short + case T_BYTE: + __ sxtb(dest_reg, dest_reg); + break; + case T_SHORT: + __ sxth(dest_reg, dest_reg); + break; + // need to move from GPR to FPR after LDAR with FMOV for floating types + case T_FLOAT: + __ fmovs(dest->as_float_reg(), dest_reg); + break; + case T_DOUBLE: + __ fmovd(dest->as_double_reg(), dest_reg); + break; + default: + break; + } +} int LIR_Assembler::array_element_size(BasicType type) const { int elem_size = type2aelembytes(type); @@ -1269,12 +1311,9 @@ void LIR_Assembler::emit_typecheck_helper(LIR_OpTypeCheck *op, Label* success, L } else if (obj == klass_RInfo) { klass_RInfo = dst; } - if (k->is_loaded() && !UseCompressedClassPointers) { - select_different_registers(obj, dst, k_RInfo, klass_RInfo); - } else { - Rtmp1 = op->tmp3()->as_register(); - select_different_registers(obj, dst, k_RInfo, klass_RInfo, Rtmp1); - } + + Rtmp1 = op->tmp3()->as_register(); + select_different_registers(obj, dst, k_RInfo, klass_RInfo, Rtmp1); assert_different_registers(obj, k_RInfo, klass_RInfo); @@ -2778,7 +2817,9 @@ void LIR_Assembler::rt_call(LIR_Opr result, address dest, const LIR_OprList* arg } void LIR_Assembler::volatile_move_op(LIR_Opr src, LIR_Opr dest, BasicType type, CodeEmitInfo* info) { - if (dest->is_address() || src->is_address()) { + if (src->is_address()) { + mem2reg(src, dest, type, lir_patch_none, info, /*wide*/false, /*is_volatile*/true); + } else if (dest->is_address()) { move_op(src, dest, type, lir_patch_none, info, /*wide*/false); } else { ShouldNotReachHere(); diff --git a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.hpp index 5af06fc6a1c..367256d2f69 100644 --- a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.hpp @@ -57,6 +57,12 @@ friend class ArrayCopyStub; void casw(Register addr, Register newval, Register cmpval); void casl(Register addr, Register newval, Register cmpval); + void mem2reg(LIR_Opr src, LIR_Opr dest, BasicType type, + LIR_PatchCode patch_code, + CodeEmitInfo* info, bool wide, bool is_volatile); + void load_unordered(LIR_Address *from_addr, LIR_Opr dest, BasicType type, bool wide, CodeEmitInfo* info); + void load_volatile(LIR_Address *from_addr, LIR_Opr dest, BasicType type, CodeEmitInfo* info); + static const int max_tableswitches = 20; struct tableswitch switches[max_tableswitches]; int tableswitch_count; diff --git a/src/hotspot/cpu/aarch64/c1_LIRGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_LIRGenerator_aarch64.cpp index ad26d494b2d..7e82f410a95 100644 --- a/src/hotspot/cpu/aarch64/c1_LIRGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_LIRGenerator_aarch64.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, 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. * @@ -1287,9 +1287,7 @@ void LIRGenerator::do_CheckCast(CheckCast* x) { } LIR_Opr reg = rlock_result(x); LIR_Opr tmp3 = LIR_OprFact::illegalOpr; - if (!x->klass()->is_loaded() || UseCompressedClassPointers) { - tmp3 = new_register(objectType); - } + tmp3 = new_register(objectType); __ checkcast(reg, obj.result(), x->klass(), new_register(objectType), new_register(objectType), tmp3, x->direct_compare(), info_for_exception, patching_info, stub, @@ -1308,9 +1306,7 @@ void LIRGenerator::do_InstanceOf(InstanceOf* x) { } obj.load_item(); LIR_Opr tmp3 = LIR_OprFact::illegalOpr; - if (!x->klass()->is_loaded() || UseCompressedClassPointers) { - tmp3 = new_register(objectType); - } + tmp3 = new_register(objectType); __ instanceof(reg, obj.result(), x->klass(), new_register(objectType), new_register(objectType), tmp3, x->direct_compare(), patching_info, x->profiled_method(), x->profiled_bci()); @@ -1402,14 +1398,5 @@ void LIRGenerator::volatile_field_store(LIR_Opr value, LIR_Address* address, void LIRGenerator::volatile_field_load(LIR_Address* address, LIR_Opr result, CodeEmitInfo* info) { - // 8179954: We need to make sure that the code generated for - // volatile accesses forms a sequentially-consistent set of - // operations when combined with STLR and LDAR. Without a leading - // membar it's possible for a simple Dekker test to fail if loads - // use LD;DMB but stores use STLR. This can happen if C2 compiles - // the stores in one method and C1 compiles the loads in another. - if (!CompilerConfig::is_c1_only_no_jvmci()) { - __ membar(); - } __ volatile_load_mem_reg(address, result, info); } diff --git a/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp index e934632715c..89a9422ea48 100644 --- a/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2021, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -105,12 +105,8 @@ void C1_MacroAssembler::initialize_header(Register obj, Register klass, Register } else { mov(t1, checked_cast(markWord::prototype().value())); str(t1, Address(obj, oopDesc::mark_offset_in_bytes())); - if (UseCompressedClassPointers) { // Take care not to kill klass - encode_klass_not_null(t1, klass); - strw(t1, Address(obj, oopDesc::klass_offset_in_bytes())); - } else { - str(klass, Address(obj, oopDesc::klass_offset_in_bytes())); - } + encode_klass_not_null(t1, klass); // Take care not to kill klass + strw(t1, Address(obj, oopDesc::klass_offset_in_bytes())); } if (len->is_valid()) { @@ -121,7 +117,7 @@ void C1_MacroAssembler::initialize_header(Register obj, Register klass, Register // Clear gap/first 4 bytes following the length field. strw(zr, Address(obj, base_offset)); } - } else if (UseCompressedClassPointers && !UseCompactObjectHeaders) { + } else if (!UseCompactObjectHeaders) { store_klass_gap(obj, zr); } } diff --git a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp index 7aab7d389e1..3c179f21c14 100644 --- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright 2026 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 @@ -1883,6 +1884,27 @@ void C2_MacroAssembler::neon_reduce_mul_fp(FloatRegister dst, BasicType bt, BLOCK_COMMENT("neon_reduce_mul_fp {"); switch(bt) { + // The T_SHORT type below is for Float16 type which also uses floating-point + // instructions. + case T_SHORT: + fmulh(dst, fsrc, vsrc); + ext(vtmp, T8B, vsrc, vsrc, 2); + fmulh(dst, dst, vtmp); + ext(vtmp, T8B, vsrc, vsrc, 4); + fmulh(dst, dst, vtmp); + ext(vtmp, T8B, vsrc, vsrc, 6); + fmulh(dst, dst, vtmp); + if (isQ) { + ext(vtmp, T16B, vsrc, vsrc, 8); + fmulh(dst, dst, vtmp); + ext(vtmp, T16B, vsrc, vsrc, 10); + fmulh(dst, dst, vtmp); + ext(vtmp, T16B, vsrc, vsrc, 12); + fmulh(dst, dst, vtmp); + ext(vtmp, T16B, vsrc, vsrc, 14); + fmulh(dst, dst, vtmp); + } + break; case T_FLOAT: fmuls(dst, fsrc, vsrc); ins(vtmp, S, vsrc, 0, 1); @@ -1907,6 +1929,33 @@ void C2_MacroAssembler::neon_reduce_mul_fp(FloatRegister dst, BasicType bt, BLOCK_COMMENT("} neon_reduce_mul_fp"); } +// Vector reduction add for half float type with ASIMD instructions. +void C2_MacroAssembler::neon_reduce_add_fp16(FloatRegister dst, FloatRegister fsrc, FloatRegister vsrc, + unsigned vector_length_in_bytes, FloatRegister vtmp) { + assert(vector_length_in_bytes == 8 || vector_length_in_bytes == 16, "unsupported"); + bool isQ = vector_length_in_bytes == 16; + + BLOCK_COMMENT("neon_reduce_add_fp16 {"); + faddh(dst, fsrc, vsrc); + ext(vtmp, T8B, vsrc, vsrc, 2); + faddh(dst, dst, vtmp); + ext(vtmp, T8B, vsrc, vsrc, 4); + faddh(dst, dst, vtmp); + ext(vtmp, T8B, vsrc, vsrc, 6); + faddh(dst, dst, vtmp); + if (isQ) { + ext(vtmp, T16B, vsrc, vsrc, 8); + faddh(dst, dst, vtmp); + ext(vtmp, T16B, vsrc, vsrc, 10); + faddh(dst, dst, vtmp); + ext(vtmp, T16B, vsrc, vsrc, 12); + faddh(dst, dst, vtmp); + ext(vtmp, T16B, vsrc, vsrc, 14); + faddh(dst, dst, vtmp); + } + BLOCK_COMMENT("} neon_reduce_add_fp16"); +} + // Helper to select logical instruction void C2_MacroAssembler::neon_reduce_logical_helper(int opc, bool is64, Register Rd, Register Rn, Register Rm, @@ -2414,17 +2463,17 @@ void C2_MacroAssembler::neon_rearrange_hsd(FloatRegister dst, FloatRegister src, break; case T_LONG: case T_DOUBLE: - // Load the iota indices for Long type. The indices are ordered by - // type B/S/I/L/F/D, and the offset between two types is 16; Hence - // the offset for L is 48. - lea(rscratch1, - ExternalAddress(StubRoutines::aarch64::vector_iota_indices() + 48)); - ldrq(tmp, rscratch1); - // Check whether the input "shuffle" is the same with iota indices. - // Return "src" if true, otherwise swap the two elements of "src". - cm(EQ, dst, size2, shuffle, tmp); - ext(tmp, size1, src, src, 8); - bsl(dst, size1, src, tmp); + { + int idx = vector_iota_entry_index(T_LONG); + lea(rscratch1, + ExternalAddress(StubRoutines::aarch64::vector_iota_indices(idx))); + ldrq(tmp, rscratch1); + // Check whether the input "shuffle" is the same with iota indices. + // Return "src" if true, otherwise swap the two elements of "src". + cm(EQ, dst, size2, shuffle, tmp); + ext(tmp, size1, src, src, 8); + bsl(dst, size1, src, tmp); + } break; default: assert(false, "unsupported element type"); @@ -2896,3 +2945,24 @@ void C2_MacroAssembler::sve_cpy(FloatRegister dst, SIMD_RegVariant T, } Assembler::sve_cpy(dst, T, pg, imm8, isMerge); } + +int C2_MacroAssembler::vector_iota_entry_index(BasicType bt) { + // The vector iota entries array is ordered by type B/S/I/L/F/D, and + // the offset between two types is 16. + switch(bt) { + case T_BYTE: + return 0; + case T_SHORT: + return 1; + case T_INT: + return 2; + case T_LONG: + return 3; + case T_FLOAT: + return 4; + case T_DOUBLE: + return 5; + default: + ShouldNotReachHere(); + } +} diff --git a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp index 5c05832afbe..f96d3ffb863 100644 --- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp @@ -177,6 +177,9 @@ FloatRegister fsrc, FloatRegister vsrc, unsigned vector_length_in_bytes, FloatRegister vtmp); + void neon_reduce_add_fp16(FloatRegister dst, FloatRegister fsrc, FloatRegister vsrc, + unsigned vector_length_in_bytes, FloatRegister vtmp); + void neon_reduce_logical(int opc, Register dst, BasicType bt, Register isrc, FloatRegister vsrc, unsigned vector_length_in_bytes); @@ -249,4 +252,5 @@ void sve_cpy(FloatRegister dst, SIMD_RegVariant T, PRegister pg, int imm8, bool isMerge); + int vector_iota_entry_index(BasicType bt); #endif // CPU_AARCH64_C2_MACROASSEMBLER_AARCH64_HPP diff --git a/src/hotspot/cpu/aarch64/compiledIC_aarch64.cpp b/src/hotspot/cpu/aarch64/compiledIC_aarch64.cpp index 6fe3315014b..640cd495383 100644 --- a/src/hotspot/cpu/aarch64/compiledIC_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/compiledIC_aarch64.cpp @@ -89,16 +89,21 @@ void CompiledDirectCall::set_to_interpreted(const methodHandle& callee, address NativeMovConstReg* method_holder = nativeMovConstReg_at(stub + NativeInstruction::instruction_size); + // In AOT "production" run we have mixture of AOTed and normal JITed code. + // Static call stub in AOTed nmethod always has far jump. + // Normal JITed nmethod may have short or far jump depending on distance. + // Determine actual jump instruction we have in code. + address next_instr = method_holder->next_instruction_address(); + bool is_general_jump = nativeInstruction_at(next_instr)->is_general_jump(); + #ifdef ASSERT - NativeJump* jump = MacroAssembler::codestub_branch_needs_far_jump() - ? nativeGeneralJump_at(method_holder->next_instruction_address()) - : nativeJump_at(method_holder->next_instruction_address()); + NativeJump* jump = is_general_jump ? nativeGeneralJump_at(next_instr) : nativeJump_at(next_instr); verify_mt_safe(callee, entry, method_holder, jump); #endif // Update stub. method_holder->set_data((intptr_t)callee()); - MacroAssembler::pd_patch_instruction(method_holder->next_instruction_address(), entry); + MacroAssembler::pd_patch_instruction(next_instr, entry); ICache::invalidate_range(stub, to_interp_stub_size()); // Update jump to call. set_destination_mt_safe(stub); diff --git a/src/hotspot/cpu/aarch64/gc/shared/cardTableBarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shared/cardTableBarrierSetAssembler_aarch64.cpp index 0bfc320179d..7ce4e0f8aed 100644 --- a/src/hotspot/cpu/aarch64/gc/shared/cardTableBarrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/shared/cardTableBarrierSetAssembler_aarch64.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -56,8 +56,10 @@ void CardTableBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet d } } -void CardTableBarrierSetAssembler::store_check(MacroAssembler* masm, Register obj, Address dst) { - +void CardTableBarrierSetAssembler::store_check(MacroAssembler* masm, Register obj, Register tmp1, Register tmp2) { + precond(tmp1 != noreg); + precond(tmp2 != noreg); + assert_different_registers(obj, tmp1, tmp2); BarrierSet* bs = BarrierSet::barrier_set(); assert(bs->kind() == BarrierSet::CardTableBarrierSet, "Wrong barrier set kind"); @@ -65,16 +67,16 @@ void CardTableBarrierSetAssembler::store_check(MacroAssembler* masm, Register ob assert(CardTable::dirty_card_val() == 0, "must be"); - __ load_byte_map_base(rscratch1); + __ load_byte_map_base(tmp1); if (UseCondCardMark) { Label L_already_dirty; - __ ldrb(rscratch2, Address(obj, rscratch1)); - __ cbz(rscratch2, L_already_dirty); - __ strb(zr, Address(obj, rscratch1)); + __ ldrb(tmp2, Address(obj, tmp1)); + __ cbz(tmp2, L_already_dirty); + __ strb(zr, Address(obj, tmp1)); __ bind(L_already_dirty); } else { - __ strb(zr, Address(obj, rscratch1)); + __ strb(zr, Address(obj, tmp1)); } } @@ -112,10 +114,10 @@ void CardTableBarrierSetAssembler::oop_store_at(MacroAssembler* masm, DecoratorS if (needs_post_barrier) { // flatten object address if needed if (!precise || (dst.index() == noreg && dst.offset() == 0)) { - store_check(masm, dst.base(), dst); + store_check(masm, dst.base(), tmp1, tmp2); } else { __ lea(tmp3, dst); - store_check(masm, tmp3, dst); + store_check(masm, tmp3, tmp1, tmp2); } } } diff --git a/src/hotspot/cpu/aarch64/gc/shared/cardTableBarrierSetAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/gc/shared/cardTableBarrierSetAssembler_aarch64.hpp index 07dd8eb5565..07016381f78 100644 --- a/src/hotspot/cpu/aarch64/gc/shared/cardTableBarrierSetAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/gc/shared/cardTableBarrierSetAssembler_aarch64.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -46,7 +46,7 @@ protected: virtual void store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, Address dst, Register val, Register tmp1, Register tmp2, Register tmp3); - void store_check(MacroAssembler* masm, Register obj, Address dst); + void store_check(MacroAssembler* masm, Register obj, Register tmp1, Register tmp2); }; #endif // CPU_AARCH64_GC_SHARED_CARDTABLEBARRIERSETASSEMBLER_AARCH64_HPP diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/c1/shenandoahBarrierSetC1_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shenandoah/c1/shenandoahBarrierSetC1_aarch64.cpp index e4db8a9ab1f..e31a58243b5 100644 --- a/src/hotspot/cpu/aarch64/gc/shenandoah/c1/shenandoahBarrierSetC1_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/shenandoah/c1/shenandoahBarrierSetC1_aarch64.cpp @@ -50,14 +50,10 @@ void LIR_OpShenandoahCompareAndSwap::emit_code(LIR_Assembler* masm) { ShenandoahBarrierSet::assembler()->cmpxchg_oop(masm->masm(), addr, cmpval, newval, /*acquire*/ true, /*release*/ true, /*is_cae*/ false, result); - if (CompilerConfig::is_c1_only_no_jvmci()) { - // The membar here is necessary to prevent reordering between the - // release store in the CAS above and a subsequent volatile load. - // However for tiered compilation C1 inserts a full barrier before - // volatile loads which means we don't need an additional barrier - // here (see LIRGenerator::volatile_field_load()). - __ membar(__ AnyAny); - } + // The membar here is necessary to prevent reordering between the + // release store in the CAS above and a subsequent volatile load. + // See also: LIR_Assembler::casw, LIR_Assembler::casl. + __ membar(__ AnyAny); } #undef __ diff --git a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp index 4f0977a414f..f0885fee93d 100644 --- a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp @@ -879,7 +879,9 @@ void ZBarrierSetAssembler::patch_barrier_relocation(address addr, int format) { ShouldNotReachHere(); } - ICache::invalidate_word((address)patch_addr); + if (!UseSingleICacheInvalidation) { + ICache::invalidate_word((address)patch_addr); + } } #ifdef COMPILER1 diff --git a/src/hotspot/cpu/aarch64/globals_aarch64.hpp b/src/hotspot/cpu/aarch64/globals_aarch64.hpp index e6de2c798b1..dfeba73bede 100644 --- a/src/hotspot/cpu/aarch64/globals_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/globals_aarch64.hpp @@ -115,18 +115,26 @@ define_pd_global(intx, InlineSmallCode, 1000); "Value -1 means off.") \ range(-1, 4096) \ product(ccstr, OnSpinWaitInst, "yield", DIAGNOSTIC, \ - "The instruction to use to implement " \ - "java.lang.Thread.onSpinWait()." \ - "Valid values are: none, nop, isb, yield, sb.") \ + "The instruction to use for java.lang.Thread.onSpinWait(). " \ + "Valid values are: none, nop, isb, yield, sb, wfet.") \ constraint(OnSpinWaitInstNameConstraintFunc, AtParse) \ product(uint, OnSpinWaitInstCount, 1, DIAGNOSTIC, \ - "The number of OnSpinWaitInst instructions to generate." \ - "It cannot be used with OnSpinWaitInst=none.") \ + "The number of OnSpinWaitInst instructions to generate. " \ + "It cannot be used with OnSpinWaitInst=none. " \ + "For OnSpinWaitInst=wfet it must be 1.") \ range(1, 99) \ + product(uint, OnSpinWaitDelay, 40, DIAGNOSTIC, \ + "The minimum delay (in nanoseconds) of the OnSpinWait loop. " \ + "It can only be used with -XX:OnSpinWaitInst=wfet.") \ + range(1, 1000) \ product(ccstr, UseBranchProtection, "none", \ "Branch Protection to use: none, standard, pac-ret") \ product(bool, AlwaysMergeDMB, true, DIAGNOSTIC, \ "Always merge DMB instructions in code emission") \ + product(bool, NeoverseN1ICacheErratumMitigation, false, DIAGNOSTIC, \ + "Enable workaround for Neoverse N1 erratum 1542419") \ + product(bool, UseSingleICacheInvalidation, false, DIAGNOSTIC, \ + "Defer multiple ICache invalidation to single invalidation") \ // end of ARCH_FLAGS diff --git a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp index 2b506b241e0..980fedb406d 100644 --- a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2020, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -989,26 +989,15 @@ void InterpreterMacroAssembler::profile_final_call(Register mdp) { void InterpreterMacroAssembler::profile_virtual_call(Register receiver, - Register mdp, - bool receiver_can_be_null) { + Register mdp) { if (ProfileInterpreter) { Label profile_continue; // If no method data exists, go to profile_continue. test_method_data_pointer(mdp, profile_continue); - Label skip_receiver_profile; - if (receiver_can_be_null) { - Label not_null; - // We are making a call. Increment the count for null receiver. - increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset())); - b(skip_receiver_profile); - bind(not_null); - } - // Record the receiver type. profile_receiver_type(receiver, mdp, 0); - bind(skip_receiver_profile); // The method data pointer needs to be updated to reflect the new target. update_mdp_by_constant(mdp, in_bytes(VirtualCallData::virtual_call_data_size())); diff --git a/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp b/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp index 74d4430000d..9a074f1ce69 100644 --- a/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2015, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -285,8 +285,7 @@ class InterpreterMacroAssembler: public MacroAssembler { void profile_not_taken_branch(Register mdp); void profile_call(Register mdp); void profile_final_call(Register mdp); - void profile_virtual_call(Register receiver, Register mdp, - bool receiver_can_be_null = false); + void profile_virtual_call(Register receiver, Register mdp); void profile_ret(Register return_bci, Register mdp); void profile_null_seen(Register mdp); void profile_typecheck(Register mdp, Register klass); diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index 3e3e95be07e..7bec0a3c0ca 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -55,6 +55,7 @@ #include "runtime/sharedRuntime.hpp" #include "runtime/stubRoutines.hpp" #include "utilities/globalDefinitions.hpp" +#include "utilities/integerCast.hpp" #include "utilities/powerOfTwo.hpp" #ifdef COMPILER1 #include "c1/c1_LIRAssembler.hpp" @@ -762,7 +763,7 @@ void MacroAssembler::call_VM_base(Register oop_result, assert(java_thread == rthread, "unexpected register"); #ifdef ASSERT // TraceBytecodes does not use r12 but saves it over the call, so don't verify - // if ((UseCompressedOops || UseCompressedClassPointers) && !TraceBytecodes) verify_heapbase("call_VM_base: heap base corrupted?"); + // if (!TraceBytecodes) verify_heapbase("call_VM_base: heap base corrupted?"); #endif // ASSERT assert(java_thread != oop_result , "cannot use the same register for java_thread & oop_result"); @@ -952,7 +953,10 @@ void MacroAssembler::emit_static_call_stub() { } int MacroAssembler::static_call_stub_size() { - if (!codestub_branch_needs_far_jump()) { + // During AOT production run AOT and JIT compiled code + // are used at the same time. We need this size + // to be the same for both types of code. + if (!codestub_branch_needs_far_jump() && !AOTCodeCache::is_on_for_use()) { // isb; movk; movz; movz; b return 5 * NativeInstruction::instruction_size; } @@ -1002,14 +1006,10 @@ int MacroAssembler::ic_check(int end_alignment) { load_narrow_klass_compact(tmp1, receiver); ldrw(tmp2, Address(data, CompiledICData::speculated_klass_offset())); cmpw(tmp1, tmp2); - } else if (UseCompressedClassPointers) { + } else { ldrw(tmp1, Address(receiver, oopDesc::klass_offset_in_bytes())); ldrw(tmp2, Address(data, CompiledICData::speculated_klass_offset())); cmpw(tmp1, tmp2); - } else { - ldr(tmp1, Address(receiver, oopDesc::klass_offset_in_bytes())); - ldr(tmp2, Address(data, CompiledICData::speculated_klass_offset())); - cmp(tmp1, tmp2); } Label dont; @@ -2917,7 +2917,11 @@ void MacroAssembler::increment(Address dst, int value) // Push lots of registers in the bit set supplied. Don't push sp. // Return the number of words pushed -int MacroAssembler::push(unsigned int bitset, Register stack) { +int MacroAssembler::push(RegSet regset, Register stack) { + if (regset.bits() == 0) { + return 0; + } + auto bitset = integer_cast(regset.bits()); int words_pushed = 0; // Scan bitset to accumulate register pairs @@ -2947,7 +2951,11 @@ int MacroAssembler::push(unsigned int bitset, Register stack) { return count; } -int MacroAssembler::pop(unsigned int bitset, Register stack) { +int MacroAssembler::pop(RegSet regset, Register stack) { + if (regset.bits() == 0) { + return 0; + } + auto bitset = integer_cast(regset.bits()); int words_pushed = 0; // Scan bitset to accumulate register pairs @@ -2979,7 +2987,11 @@ int MacroAssembler::pop(unsigned int bitset, Register stack) { // Push lots of registers in the bit set supplied. Don't push sp. // Return the number of dwords pushed -int MacroAssembler::push_fp(unsigned int bitset, Register stack, FpPushPopMode mode) { +int MacroAssembler::push_fp(FloatRegSet regset, Register stack, FpPushPopMode mode) { + if (regset.bits() == 0) { + return 0; + } + auto bitset = integer_cast(regset.bits()); int words_pushed = 0; bool use_sve = false; int sve_vector_size_in_bytes = 0; @@ -3092,7 +3104,11 @@ int MacroAssembler::push_fp(unsigned int bitset, Register stack, FpPushPopMode m } // Return the number of dwords popped -int MacroAssembler::pop_fp(unsigned int bitset, Register stack, FpPushPopMode mode) { +int MacroAssembler::pop_fp(FloatRegSet regset, Register stack, FpPushPopMode mode) { + if (regset.bits() == 0) { + return 0; + } + auto bitset = integer_cast(regset.bits()); int words_pushed = 0; bool use_sve = false; int sve_vector_size_in_bytes = 0; @@ -3202,7 +3218,11 @@ int MacroAssembler::pop_fp(unsigned int bitset, Register stack, FpPushPopMode mo } // Return the number of dwords pushed -int MacroAssembler::push_p(unsigned int bitset, Register stack) { +int MacroAssembler::push_p(PRegSet regset, Register stack) { + if (regset.bits() == 0) { + return 0; + } + auto bitset = integer_cast(regset.bits()); bool use_sve = false; int sve_predicate_size_in_slots = 0; @@ -3239,7 +3259,11 @@ int MacroAssembler::push_p(unsigned int bitset, Register stack) { } // Return the number of dwords popped -int MacroAssembler::pop_p(unsigned int bitset, Register stack) { +int MacroAssembler::pop_p(PRegSet regset, Register stack) { + if (regset.bits() == 0) { + return 0; + } + auto bitset = integer_cast(regset.bits()); bool use_sve = false; int sve_predicate_size_in_slots = 0; @@ -3278,7 +3302,6 @@ int MacroAssembler::pop_p(unsigned int bitset, Register stack) { #ifdef ASSERT void MacroAssembler::verify_heapbase(const char* msg) { #if 0 - assert (UseCompressedOops || UseCompressedClassPointers, "should be compressed"); assert (Universe::heap() != nullptr, "java heap should be initialized"); if (!UseCompressedOops || Universe::ptr_base() == nullptr) { // rheapbase is allocated as general register @@ -3456,7 +3479,7 @@ void MacroAssembler::subw(Register Rd, Register Rn, RegisterOrConstant decrement void MacroAssembler::reinit_heapbase() { if (UseCompressedOops) { - if (Universe::is_fully_initialized()) { + if (Universe::is_fully_initialized() && !AOTCodeCache::is_on_for_dump()) { mov(rheapbase, CompressedOops::base()); } else { lea(rheapbase, ExternalAddress(CompressedOops::base_addr())); @@ -5067,13 +5090,10 @@ void MacroAssembler::load_narrow_klass_compact(Register dst, Register src) { void MacroAssembler::load_klass(Register dst, Register src) { if (UseCompactObjectHeaders) { load_narrow_klass_compact(dst, src); - decode_klass_not_null(dst); - } else if (UseCompressedClassPointers) { - ldrw(dst, Address(src, oopDesc::klass_offset_in_bytes())); - decode_klass_not_null(dst); } else { - ldr(dst, Address(src, oopDesc::klass_offset_in_bytes())); + ldrw(dst, Address(src, oopDesc::klass_offset_in_bytes())); } + decode_klass_not_null(dst); } void MacroAssembler::restore_cpu_control_state_after_jni(Register tmp1, Register tmp2) { @@ -5125,25 +5145,22 @@ void MacroAssembler::load_mirror(Register dst, Register method, Register tmp1, R void MacroAssembler::cmp_klass(Register obj, Register klass, Register tmp) { assert_different_registers(obj, klass, tmp); - if (UseCompressedClassPointers) { - if (UseCompactObjectHeaders) { - load_narrow_klass_compact(tmp, obj); - } else { - ldrw(tmp, Address(obj, oopDesc::klass_offset_in_bytes())); - } - if (CompressedKlassPointers::base() == nullptr) { - cmp(klass, tmp, LSL, CompressedKlassPointers::shift()); - return; - } else if (((uint64_t)CompressedKlassPointers::base() & 0xffffffff) == 0 - && CompressedKlassPointers::shift() == 0) { - // Only the bottom 32 bits matter - cmpw(klass, tmp); - return; - } - decode_klass_not_null(tmp); + if (UseCompactObjectHeaders) { + load_narrow_klass_compact(tmp, obj); } else { - ldr(tmp, Address(obj, oopDesc::klass_offset_in_bytes())); + ldrw(tmp, Address(obj, oopDesc::klass_offset_in_bytes())); } + if (CompressedKlassPointers::base() == nullptr) { + cmp(klass, tmp, LSL, CompressedKlassPointers::shift()); + return; + } else if (!AOTCodeCache::is_on_for_dump() && + ((uint64_t)CompressedKlassPointers::base() & 0xffffffff) == 0 + && CompressedKlassPointers::shift() == 0) { + // Only the bottom 32 bits matter + cmpw(klass, tmp); + return; + } + decode_klass_not_null(tmp); cmp(klass, tmp); } @@ -5151,36 +5168,25 @@ void MacroAssembler::cmp_klasses_from_objects(Register obj1, Register obj2, Regi if (UseCompactObjectHeaders) { load_narrow_klass_compact(tmp1, obj1); load_narrow_klass_compact(tmp2, obj2); - cmpw(tmp1, tmp2); - } else if (UseCompressedClassPointers) { + } else { ldrw(tmp1, Address(obj1, oopDesc::klass_offset_in_bytes())); ldrw(tmp2, Address(obj2, oopDesc::klass_offset_in_bytes())); - cmpw(tmp1, tmp2); - } else { - ldr(tmp1, Address(obj1, oopDesc::klass_offset_in_bytes())); - ldr(tmp2, Address(obj2, oopDesc::klass_offset_in_bytes())); - cmp(tmp1, tmp2); } + cmpw(tmp1, tmp2); } void MacroAssembler::store_klass(Register dst, Register src) { // FIXME: Should this be a store release? concurrent gcs assumes // klass length is valid if klass field is not null. assert(!UseCompactObjectHeaders, "not with compact headers"); - if (UseCompressedClassPointers) { - encode_klass_not_null(src); - strw(src, Address(dst, oopDesc::klass_offset_in_bytes())); - } else { - str(src, Address(dst, oopDesc::klass_offset_in_bytes())); - } + encode_klass_not_null(src); + strw(src, Address(dst, oopDesc::klass_offset_in_bytes())); } void MacroAssembler::store_klass_gap(Register dst, Register src) { assert(!UseCompactObjectHeaders, "not with compact headers"); - if (UseCompressedClassPointers) { - // Store to klass gap in destination - strw(src, Address(dst, oopDesc::klass_gap_offset_in_bytes())); - } + // Store to klass gap in destination + strw(src, Address(dst, oopDesc::klass_gap_offset_in_bytes())); } // Algorithm must match CompressedOops::encode. @@ -5326,8 +5332,6 @@ MacroAssembler::KlassDecodeMode MacroAssembler::klass_decode_mode() { } MacroAssembler::KlassDecodeMode MacroAssembler::klass_decode_mode(address base, int shift, const size_t range) { - assert(UseCompressedClassPointers, "not using compressed class pointers"); - // KlassDecodeMode shouldn't be set already. assert(_klass_decode_mode == KlassDecodeNone, "set once"); @@ -5393,7 +5397,7 @@ void MacroAssembler::encode_klass_not_null_for_aot(Register dst, Register src) { } void MacroAssembler::encode_klass_not_null(Register dst, Register src) { - if (AOTCodeCache::is_on_for_dump()) { + if (CompressedKlassPointers::base() != nullptr && AOTCodeCache::is_on_for_dump()) { encode_klass_not_null_for_aot(dst, src); return; } @@ -5457,8 +5461,6 @@ void MacroAssembler::decode_klass_not_null_for_aot(Register dst, Register src) { } void MacroAssembler::decode_klass_not_null(Register dst, Register src) { - assert (UseCompressedClassPointers, "should only be used for compressed headers"); - if (AOTCodeCache::is_on_for_dump()) { decode_klass_not_null_for_aot(dst, src); return; @@ -5525,7 +5527,6 @@ void MacroAssembler::set_narrow_oop(Register dst, jobject obj) { } void MacroAssembler::set_narrow_klass(Register dst, Klass* k) { - assert (UseCompressedClassPointers, "should only be used for compressed headers"); assert (oop_recorder() != nullptr, "this assembler needs an OopRecorder"); int index = oop_recorder()->find_index(k); @@ -6835,6 +6836,9 @@ void MacroAssembler::spin_wait() { assert(VM_Version::supports_sb(), "current CPU does not support SB instruction"); sb(); break; + case SpinWait::WFET: + spin_wait_wfet(VM_Version::spin_wait_desc().delay()); + break; default: ShouldNotReachHere(); } @@ -6842,6 +6846,28 @@ void MacroAssembler::spin_wait() { block_comment("}"); } +void MacroAssembler::spin_wait_wfet(int delay_ns) { + // The sequence assumes CNTFRQ_EL0 is fixed to 1GHz. The assumption is valid + // starting from Armv8.6, according to the "D12.1.2 The system counter" of the + // Arm Architecture Reference Manual for A-profile architecture version M.a.a. + // This is sufficient because FEAT_WFXT is introduced from Armv8.6. + Register target = rscratch1; + Register current = rscratch2; + get_cntvctss_el0(current); + add(target, current, delay_ns); + + Label L_wait_loop; + bind(L_wait_loop); + + wfet(target); + get_cntvctss_el0(current); + + cmp(current, target); + br(LT, L_wait_loop); + + sb(); +} + // Stack frame creation/removal void MacroAssembler::enter(bool strip_ret_addr) { diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp index fa32f3055b9..a6cc862d05c 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp @@ -499,29 +499,20 @@ private: void mov_immediate64(Register dst, uint64_t imm64); void mov_immediate32(Register dst, uint32_t imm32); - int push(unsigned int bitset, Register stack); - int pop(unsigned int bitset, Register stack); - - int push_fp(unsigned int bitset, Register stack, FpPushPopMode mode); - int pop_fp(unsigned int bitset, Register stack, FpPushPopMode mode); - - int push_p(unsigned int bitset, Register stack); - int pop_p(unsigned int bitset, Register stack); - void mov(Register dst, Address a); public: - void push(RegSet regs, Register stack) { if (regs.bits()) push(regs.bits(), stack); } - void pop(RegSet regs, Register stack) { if (regs.bits()) pop(regs.bits(), stack); } + int push(RegSet regset, Register stack); + int pop(RegSet regset, Register stack); - void push_fp(FloatRegSet regs, Register stack, FpPushPopMode mode = PushPopFull) { if (regs.bits()) push_fp(regs.bits(), stack, mode); } - void pop_fp(FloatRegSet regs, Register stack, FpPushPopMode mode = PushPopFull) { if (regs.bits()) pop_fp(regs.bits(), stack, mode); } + int push_fp(FloatRegSet regset, Register stack, FpPushPopMode mode = PushPopFull); + int pop_fp(FloatRegSet regset, Register stack, FpPushPopMode mode = PushPopFull); static RegSet call_clobbered_gp_registers(); - void push_p(PRegSet regs, Register stack) { if (regs.bits()) push_p(regs.bits(), stack); } - void pop_p(PRegSet regs, Register stack) { if (regs.bits()) pop_p(regs.bits(), stack); } + int push_p(PRegSet regset, Register stack); + int pop_p(PRegSet regset, Register stack); // Push and pop everything that might be clobbered by a native // runtime call except rscratch1 and rscratch2. (They are always @@ -660,6 +651,14 @@ public: msr(0b011, 0b0100, 0b0010, 0b000, reg); } + // CNTVCTSS_EL0: op1 == 011 + // CRn == 1110 + // CRm == 0000 + // op2 == 110 + inline void get_cntvctss_el0(Register reg) { + mrs(0b011, 0b1110, 0b0000, 0b110, reg); + } + // idiv variant which deals with MINLONG as dividend and -1 as divisor int corrected_idivl(Register result, Register ra, Register rb, bool want_remainder, Register tmp = rscratch1); @@ -891,10 +890,6 @@ public: // thread in the default location (rthread) void reset_last_Java_frame(bool clear_fp); - // Stores - void store_check(Register obj); // store check for obj - register is destroyed afterwards - void store_check(Register obj, Address dst); // same as above, dst is exact store location (reg. is destroyed) - void resolve_jobject(Register value, Register tmp1, Register tmp2); void resolve_global_jobject(Register value, Register tmp1, Register tmp2); @@ -1724,6 +1719,7 @@ public: // Code for java.lang.Thread::onSpinWait() intrinsic. void spin_wait(); + void spin_wait_wfet(int delay_ns); void fast_lock(Register basic_lock, Register obj, Register t1, Register t2, Register t3, Label& slow); void fast_unlock(Register obj, Register t1, Register t2, Register t3, Label& slow); diff --git a/src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp b/src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp index fc7274714ad..ab9896fa426 100644 --- a/src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp @@ -97,7 +97,7 @@ protected: #define MACOS_WX_WRITE MACOS_AARCH64_ONLY(os::thread_wx_enable_write()) void set_char_at(int offset, char c) { MACOS_WX_WRITE; *addr_at(offset) = (u_char)c; } void set_int_at(int offset, jint i) { MACOS_WX_WRITE; *(jint*)addr_at(offset) = i; } - void set_uint_at(int offset, jint i) { MACOS_WX_WRITE; *(juint*)addr_at(offset) = i; } + void set_uint_at(int offset, juint i) { MACOS_WX_WRITE; *(juint*)addr_at(offset) = i; } void set_ptr_at(int offset, address ptr) { MACOS_WX_WRITE; *(address*)addr_at(offset) = ptr; } void set_oop_at(int offset, oop o) { MACOS_WX_WRITE; *(oop*)addr_at(offset) = o; } #undef MACOS_WX_WRITE @@ -178,13 +178,11 @@ public: address destination() const; void set_destination(address dest) { - int offset = dest - instruction_address(); - unsigned int insn = 0b100101 << 26; + int64_t offset = dest - instruction_address(); + juint insn = 0b100101u << 26u; assert((offset & 3) == 0, "should be"); - offset >>= 2; - offset &= (1 << 26) - 1; // mask off insn part - insn |= offset; - set_int_at(displacement_offset, insn); + Instruction_aarch64::spatch(reinterpret_cast
(&insn), 25, 0, offset >> 2); + set_uint_at(displacement_offset, insn); } void verify_alignment() { ; } diff --git a/src/hotspot/cpu/aarch64/relocInfo_aarch64.cpp b/src/hotspot/cpu/aarch64/relocInfo_aarch64.cpp index dbec2d76d4f..f1b9fb213a2 100644 --- a/src/hotspot/cpu/aarch64/relocInfo_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/relocInfo_aarch64.cpp @@ -54,7 +54,12 @@ void Relocation::pd_set_data_value(address x, bool verify_only) { bytes = MacroAssembler::pd_patch_instruction_size(addr(), x); break; } - ICache::invalidate_range(addr(), bytes); + + if (UseSingleICacheInvalidation) { + assert(_binding != nullptr, "expect to be called with RelocIterator in use"); + } else { + ICache::invalidate_range(addr(), bytes); + } } address Relocation::pd_call_destination(address orig_addr) { diff --git a/src/hotspot/cpu/aarch64/runtime_aarch64.cpp b/src/hotspot/cpu/aarch64/runtime_aarch64.cpp index e36aa21b567..638e57b03fe 100644 --- a/src/hotspot/cpu/aarch64/runtime_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/runtime_aarch64.cpp @@ -290,7 +290,7 @@ ExceptionBlob* OptoRuntime::generate_exception_blob() { assert(SimpleRuntimeFrame::framesize % 4 == 0, "sp not 16-byte aligned"); const char* name = OptoRuntime::stub_name(StubId::c2_exception_id); - CodeBlob* blob = AOTCodeCache::load_code_blob(AOTCodeEntry::C2Blob, (uint)BlobId::c2_exception_id, name); + CodeBlob* blob = AOTCodeCache::load_code_blob(AOTCodeEntry::C2Blob, BlobId::c2_exception_id); if (blob != nullptr) { return blob->as_exception_blob(); } diff --git a/src/hotspot/cpu/aarch64/spin_wait_aarch64.cpp b/src/hotspot/cpu/aarch64/spin_wait_aarch64.cpp index 7da0151d834..97a981ab815 100644 --- a/src/hotspot/cpu/aarch64/spin_wait_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/spin_wait_aarch64.cpp @@ -32,6 +32,7 @@ bool SpinWait::supports(const char *name) { strcmp(name, "isb") == 0 || strcmp(name, "yield") == 0 || strcmp(name, "sb") == 0 || + strcmp(name, "wfet") == 0 || strcmp(name, "none") == 0); } @@ -46,6 +47,8 @@ SpinWait::Inst SpinWait::from_name(const char* name) { return SpinWait::YIELD; } else if (strcmp(name, "sb") == 0) { return SpinWait::SB; + } else if (strcmp(name, "wfet") == 0) { + return SpinWait::WFET; } return SpinWait::NONE; diff --git a/src/hotspot/cpu/aarch64/spin_wait_aarch64.hpp b/src/hotspot/cpu/aarch64/spin_wait_aarch64.hpp index 0e96a4b7157..6ebcd2477a8 100644 --- a/src/hotspot/cpu/aarch64/spin_wait_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/spin_wait_aarch64.hpp @@ -24,6 +24,8 @@ #ifndef CPU_AARCH64_SPIN_WAIT_AARCH64_HPP #define CPU_AARCH64_SPIN_WAIT_AARCH64_HPP +#include "utilities/debug.hpp" + class SpinWait { public: enum Inst { @@ -31,21 +33,30 @@ public: NOP, ISB, YIELD, - SB + SB, + WFET }; private: Inst _inst; int _count; + int _delay; Inst from_name(const char *name); public: - SpinWait(Inst inst = NONE, int count = 0) : _inst(inst), _count(inst == NONE ? 0 : count) {} - SpinWait(const char *name, int count) : SpinWait(from_name(name), count) {} + SpinWait(Inst inst = NONE, int count = 0, int delay = -1) + : _inst(inst), _count(inst == NONE ? 0 : count), _delay(delay) {} + SpinWait(const char *name, int count, int delay) + : SpinWait(from_name(name), count, delay) {} Inst inst() const { return _inst; } int inst_count() const { return _count; } + int delay() const { + assert(_inst == WFET, "Specifying the delay value is only supported for WFET"); + assert(_delay > 0, "The delay value must be positive"); + return _delay; + } static bool supports(const char *name); }; diff --git a/src/hotspot/cpu/aarch64/stubDeclarations_aarch64.hpp b/src/hotspot/cpu/aarch64/stubDeclarations_aarch64.hpp index 695534604b8..d1f59e479db 100644 --- a/src/hotspot/cpu/aarch64/stubDeclarations_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/stubDeclarations_aarch64.hpp @@ -29,32 +29,39 @@ #define STUBGEN_PREUNIVERSE_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(preuniverse, 0) \ #define STUBGEN_INITIAL_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(initial, 10000) \ #define STUBGEN_CONTINUATION_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(continuation, 2000) \ +// count needed for declaration of vector_iota_indices stub +#define VECTOR_IOTA_COUNT 6 #define STUBGEN_COMPILER_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(compiler, 70000) \ do_stub(compiler, vector_iota_indices) \ - do_arch_entry(aarch64, compiler, vector_iota_indices, \ - vector_iota_indices, vector_iota_indices) \ + do_arch_entry_array(aarch64, compiler, vector_iota_indices, \ + vector_iota_indices, vector_iota_indices, \ + VECTOR_IOTA_COUNT) \ do_stub(compiler, large_array_equals) \ do_arch_entry(aarch64, compiler, large_array_equals, \ large_array_equals, large_array_equals) \ @@ -84,8 +91,7 @@ do_stub(compiler, count_positives) \ do_arch_entry(aarch64, compiler, count_positives, count_positives, \ count_positives) \ - do_stub(compiler, count_positives_long) \ - do_arch_entry(aarch64, compiler, count_positives_long, \ + do_arch_entry(aarch64, compiler, count_positives, \ count_positives_long, count_positives_long) \ do_stub(compiler, compare_long_string_LL) \ do_arch_entry(aarch64, compiler, compare_long_string_LL, \ @@ -108,14 +114,16 @@ do_stub(compiler, string_indexof_linear_ul) \ do_arch_entry(aarch64, compiler, string_indexof_linear_ul, \ string_indexof_linear_ul, string_indexof_linear_ul) \ - /* this uses the entry for ghash_processBlocks */ \ - do_stub(compiler, ghash_processBlocks_wide) \ + do_stub(compiler, ghash_processBlocks_small) \ + do_arch_entry(aarch64, compiler, ghash_processBlocks_small, \ + ghash_processBlocks_small, ghash_processBlocks_small) \ #define STUBGEN_FINAL_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(final, 20000 ZGC_ONLY(+85000)) \ do_stub(final, copy_byte_f) \ do_arch_entry(aarch64, final, copy_byte_f, copy_byte_f, \ @@ -139,9 +147,49 @@ do_stub(final, spin_wait) \ do_arch_entry_init(aarch64, final, spin_wait, spin_wait, \ spin_wait, empty_spin_wait) \ - /* stub only -- entries are not stored in StubRoutines::aarch64 */ \ /* n.b. these are not the same as the generic atomic stubs */ \ do_stub(final, atomic_entry_points) \ + do_arch_entry(aarch64, final, atomic_entry_points, \ + atomic_fetch_add_4_impl, atomic_fetch_add_4_impl) \ + do_arch_entry(aarch64, final, atomic_entry_points, \ + atomic_fetch_add_8_impl, atomic_fetch_add_8_impl) \ + do_arch_entry(aarch64, final, atomic_entry_points, \ + atomic_fetch_add_4_relaxed_impl, \ + atomic_fetch_add_4_relaxed_impl) \ + do_arch_entry(aarch64, final, atomic_entry_points, \ + atomic_fetch_add_8_relaxed_impl, \ + atomic_fetch_add_8_relaxed_impl) \ + do_arch_entry(aarch64, final, atomic_entry_points, \ + atomic_xchg_4_impl, atomic_xchg_4_impl) \ + do_arch_entry(aarch64, final, atomic_entry_points, \ + atomic_xchg_8_impl, atomic_xchg_8_impl) \ + do_arch_entry(aarch64, final, atomic_entry_points, \ + atomic_cmpxchg_1_impl, atomic_cmpxchg_1_impl) \ + do_arch_entry(aarch64, final, atomic_entry_points, \ + atomic_cmpxchg_4_impl, atomic_cmpxchg_4_impl) \ + do_arch_entry(aarch64, final, atomic_entry_points, \ + atomic_cmpxchg_8_impl, atomic_cmpxchg_8_impl) \ + do_arch_entry(aarch64, final, atomic_entry_points, \ + atomic_cmpxchg_1_relaxed_impl, \ + atomic_cmpxchg_1_relaxed_impl) \ + do_arch_entry(aarch64, final, atomic_entry_points, \ + atomic_cmpxchg_4_relaxed_impl, \ + atomic_cmpxchg_4_relaxed_impl) \ + do_arch_entry(aarch64, final, atomic_entry_points, \ + atomic_cmpxchg_8_relaxed_impl, \ + atomic_cmpxchg_8_relaxed_impl) \ + do_arch_entry(aarch64, final, atomic_entry_points, \ + atomic_cmpxchg_4_release_impl, \ + atomic_cmpxchg_4_release_impl) \ + do_arch_entry(aarch64, final, atomic_entry_points, \ + atomic_cmpxchg_8_release_impl, \ + atomic_cmpxchg_8_release_impl) \ + do_arch_entry(aarch64, final, atomic_entry_points, \ + atomic_cmpxchg_4_seq_cst_impl, \ + atomic_cmpxchg_4_seq_cst_impl) \ + do_arch_entry(aarch64, final, atomic_entry_points, \ + atomic_cmpxchg_8_seq_cst_impl, \ + atomic_cmpxchg_8_seq_cst_impl) \ #endif // CPU_AARCH64_STUBDECLARATIONS_HPP diff --git a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp index 21a1124a8ec..fddb37b7b8d 100644 --- a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp @@ -79,6 +79,166 @@ #define BIND(label) bind(label); BLOCK_COMMENT(#label ":") +// Constant data definitions + +static const uint32_t _sha256_round_consts[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, +}; + +static const uint64_t _sha512_round_consts[80] = { + 0x428A2F98D728AE22L, 0x7137449123EF65CDL, 0xB5C0FBCFEC4D3B2FL, + 0xE9B5DBA58189DBBCL, 0x3956C25BF348B538L, 0x59F111F1B605D019L, + 0x923F82A4AF194F9BL, 0xAB1C5ED5DA6D8118L, 0xD807AA98A3030242L, + 0x12835B0145706FBEL, 0x243185BE4EE4B28CL, 0x550C7DC3D5FFB4E2L, + 0x72BE5D74F27B896FL, 0x80DEB1FE3B1696B1L, 0x9BDC06A725C71235L, + 0xC19BF174CF692694L, 0xE49B69C19EF14AD2L, 0xEFBE4786384F25E3L, + 0x0FC19DC68B8CD5B5L, 0x240CA1CC77AC9C65L, 0x2DE92C6F592B0275L, + 0x4A7484AA6EA6E483L, 0x5CB0A9DCBD41FBD4L, 0x76F988DA831153B5L, + 0x983E5152EE66DFABL, 0xA831C66D2DB43210L, 0xB00327C898FB213FL, + 0xBF597FC7BEEF0EE4L, 0xC6E00BF33DA88FC2L, 0xD5A79147930AA725L, + 0x06CA6351E003826FL, 0x142929670A0E6E70L, 0x27B70A8546D22FFCL, + 0x2E1B21385C26C926L, 0x4D2C6DFC5AC42AEDL, 0x53380D139D95B3DFL, + 0x650A73548BAF63DEL, 0x766A0ABB3C77B2A8L, 0x81C2C92E47EDAEE6L, + 0x92722C851482353BL, 0xA2BFE8A14CF10364L, 0xA81A664BBC423001L, + 0xC24B8B70D0F89791L, 0xC76C51A30654BE30L, 0xD192E819D6EF5218L, + 0xD69906245565A910L, 0xF40E35855771202AL, 0x106AA07032BBD1B8L, + 0x19A4C116B8D2D0C8L, 0x1E376C085141AB53L, 0x2748774CDF8EEB99L, + 0x34B0BCB5E19B48A8L, 0x391C0CB3C5C95A63L, 0x4ED8AA4AE3418ACBL, + 0x5B9CCA4F7763E373L, 0x682E6FF3D6B2B8A3L, 0x748F82EE5DEFB2FCL, + 0x78A5636F43172F60L, 0x84C87814A1F0AB72L, 0x8CC702081A6439ECL, + 0x90BEFFFA23631E28L, 0xA4506CEBDE82BDE9L, 0xBEF9A3F7B2C67915L, + 0xC67178F2E372532BL, 0xCA273ECEEA26619CL, 0xD186B8C721C0C207L, + 0xEADA7DD6CDE0EB1EL, 0xF57D4F7FEE6ED178L, 0x06F067AA72176FBAL, + 0x0A637DC5A2C898A6L, 0x113F9804BEF90DAEL, 0x1B710B35131C471BL, + 0x28DB77F523047D84L, 0x32CAAB7B40C72493L, 0x3C9EBE0A15C9BEBCL, + 0x431D67C49C100D4CL, 0x4CC5D4BECB3E42B6L, 0x597F299CFC657E2AL, + 0x5FCB6FAB3AD6FAECL, 0x6C44198C4A475817L +}; + +static const uint64_t _sha3_round_consts[24] = { + 0x0000000000000001L, 0x0000000000008082L, 0x800000000000808AL, + 0x8000000080008000L, 0x000000000000808BL, 0x0000000080000001L, + 0x8000000080008081L, 0x8000000000008009L, 0x000000000000008AL, + 0x0000000000000088L, 0x0000000080008009L, 0x000000008000000AL, + 0x000000008000808BL, 0x800000000000008BL, 0x8000000000008089L, + 0x8000000000008003L, 0x8000000000008002L, 0x8000000000000080L, + 0x000000000000800AL, 0x800000008000000AL, 0x8000000080008081L, + 0x8000000000008080L, 0x0000000080000001L, 0x8000000080008008L +}; + +static const uint64_t _double_keccak_round_consts[24] = { + 0x0000000000000001L, 0x0000000000008082L, 0x800000000000808AL, + 0x8000000080008000L, 0x000000000000808BL, 0x0000000080000001L, + 0x8000000080008081L, 0x8000000000008009L, 0x000000000000008AL, + 0x0000000000000088L, 0x0000000080008009L, 0x000000008000000AL, + 0x000000008000808BL, 0x800000000000008BL, 0x8000000000008089L, + 0x8000000000008003L, 0x8000000000008002L, 0x8000000000000080L, + 0x000000000000800AL, 0x800000008000000AL, 0x8000000080008081L, + 0x8000000000008080L, 0x0000000080000001L, 0x8000000080008008L +}; + +static const char _encodeBlock_toBase64[64] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' +}; + +static const char _encodeBlock_toBase64URL[64] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_' +}; + +// Non-SIMD lookup tables are mostly dumped from fromBase64 array used in java.util.Base64, +// except the trailing character '=' is also treated illegal value in this intrinsic. That +// is java.util.Base64.fromBase64['='] = -2, while fromBase(URL)64ForNoSIMD['='] = 255 here. +static const uint8_t _decodeBlock_fromBase64ForNoSIMD[256] = { + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 62u, 255u, 255u, 255u, 63u, + 52u, 53u, 54u, 55u, 56u, 57u, 58u, 59u, 60u, 61u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 11u, 12u, 13u, 14u, + 15u, 16u, 17u, 18u, 19u, 20u, 21u, 22u, 23u, 24u, 25u, 255u, 255u, 255u, 255u, 255u, + 255u, 26u, 27u, 28u, 29u, 30u, 31u, 32u, 33u, 34u, 35u, 36u, 37u, 38u, 39u, 40u, + 41u, 42u, 43u, 44u, 45u, 46u, 47u, 48u, 49u, 50u, 51u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, +}; + +static const uint8_t _decodeBlock_fromBase64URLForNoSIMD[256] = { + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 62u, 255u, 255u, + 52u, 53u, 54u, 55u, 56u, 57u, 58u, 59u, 60u, 61u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 11u, 12u, 13u, 14u, + 15u, 16u, 17u, 18u, 19u, 20u, 21u, 22u, 23u, 24u, 25u, 255u, 255u, 255u, 255u, 63u, + 255u, 26u, 27u, 28u, 29u, 30u, 31u, 32u, 33u, 34u, 35u, 36u, 37u, 38u, 39u, 40u, + 41u, 42u, 43u, 44u, 45u, 46u, 47u, 48u, 49u, 50u, 51u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, +}; + +// A legal value of base64 code is in range [0, 127]. We need two lookups +// with tbl/tbx and combine them to get the decode data. The 1st table vector +// lookup use tbl, out of range indices are set to 0 in destination. The 2nd +// table vector lookup use tbx, out of range indices are unchanged in +// destination. Input [64..126] is mapped to index [65, 127] in second lookup. +// The value of index 64 is set to 0, so that we know that we already get the +// decoded data with the 1st lookup. +static const uint8_t _decodeBlock_fromBase64ForSIMD[128] = { + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 62u, 255u, 255u, 255u, 63u, + 52u, 53u, 54u, 55u, 56u, 57u, 58u, 59u, 60u, 61u, 255u, 255u, 255u, 255u, 255u, 255u, + 0u, 255u, 0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 11u, 12u, 13u, + 14u, 15u, 16u, 17u, 18u, 19u, 20u, 21u, 22u, 23u, 24u, 25u, 255u, 255u, 255u, 255u, + 255u, 255u, 26u, 27u, 28u, 29u, 30u, 31u, 32u, 33u, 34u, 35u, 36u, 37u, 38u, 39u, + 40u, 41u, 42u, 43u, 44u, 45u, 46u, 47u, 48u, 49u, 50u, 51u, 255u, 255u, 255u, 255u, +}; + +static const uint8_t _decodeBlock_fromBase64URLForSIMD[128] = { + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 62u, 255u, 255u, + 52u, 53u, 54u, 55u, 56u, 57u, 58u, 59u, 60u, 61u, 255u, 255u, 255u, 255u, 255u, 255u, + 0u, 255u, 0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 11u, 12u, 13u, + 14u, 15u, 16u, 17u, 18u, 19u, 20u, 21u, 22u, 23u, 24u, 25u, 255u, 255u, 255u, 255u, + 63u, 255u, 26u, 27u, 28u, 29u, 30u, 31u, 32u, 33u, 34u, 35u, 36u, 37u, 38u, 39u, + 40u, 41u, 42u, 43u, 44u, 45u, 46u, 47u, 48u, 49u, 50u, 51u, 255u, 255u, 255u, 255u, +}; + + // Stub Code definitions class StubGenerator: public StubCodeGenerator { @@ -203,8 +363,17 @@ class StubGenerator: public StubCodeGenerator { "adjust this code"); StubId stub_id = StubId::stubgen_call_stub_id; + GrowableArray
entries; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 2, "sanity check"); + address start = load_archive_data(stub_id, &entries); + if (start != nullptr) { + assert(entries.length() == 1, "expected 1 extra entry"); + return_address = entries.at(0); + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); const Address sp_after_call (rfp, sp_after_call_off * wordSize); @@ -323,6 +492,7 @@ class StubGenerator: public StubCodeGenerator { // save current address for use by exception handling code return_address = __ pc(); + entries.append(return_address); // store result depending on type (everything that is not // T_OBJECT, T_LONG, T_FLOAT or T_DOUBLE is treated as T_INT) @@ -406,6 +576,9 @@ class StubGenerator: public StubCodeGenerator { __ strd(j_farg0, Address(j_rarg2, 0)); __ br(Assembler::AL, exit); + // record the stub entry and end plus the auxiliary entry + store_archive_data(stub_id, start, __ pc(), &entries); + return start; } @@ -423,8 +596,14 @@ class StubGenerator: public StubCodeGenerator { address generate_catch_exception() { StubId stub_id = StubId::stubgen_catch_exception_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); // same as in generate_call_stub(): const Address sp_after_call(rfp, sp_after_call_off * wordSize); @@ -450,7 +629,9 @@ class StubGenerator: public StubCodeGenerator { __ verify_oop(r0); __ str(r0, Address(rthread, Thread::pending_exception_offset())); - __ mov(rscratch1, (address)__FILE__); + // special case -- add file name string to AOT address table + address file = (address)AOTCodeCache::add_C_string(__FILE__); + __ lea(rscratch1, ExternalAddress(file)); __ str(rscratch1, Address(rthread, Thread::exception_file_offset())); __ movw(rscratch1, (int)__LINE__); __ strw(rscratch1, Address(rthread, Thread::exception_line_offset())); @@ -458,7 +639,10 @@ class StubGenerator: public StubCodeGenerator { // complete return to VM assert(StubRoutines::_call_stub_return_address != nullptr, "_call_stub_return_address must have been generated before"); - __ b(StubRoutines::_call_stub_return_address); + __ b(RuntimeAddress(StubRoutines::_call_stub_return_address)); + + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); return start; } @@ -479,8 +663,14 @@ class StubGenerator: public StubCodeGenerator { address generate_forward_exception() { StubId stub_id = StubId::stubgen_forward_exception_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); // Upon entry, LR points to the return address returning into // Java (interpreted or compiled) code; i.e., the return address @@ -551,6 +741,9 @@ class StubGenerator: public StubCodeGenerator { __ verify_oop(r0); __ br(r19); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -569,8 +762,14 @@ class StubGenerator: public StubCodeGenerator { // [tos + 5]: saved rscratch1 address generate_verify_oop() { StubId stub_id = StubId::stubgen_verify_oop_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label exit, error; @@ -613,33 +812,64 @@ class StubGenerator: public StubCodeGenerator { __ blr(rscratch1); __ hlt(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } // Generate indices for iota vector. - address generate_iota_indices(StubId stub_id) { + void generate_iota_indices(StubId stub_id) { + GrowableArray
entries; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == VECTOR_IOTA_COUNT, "sanity check"); + address start = load_archive_data(stub_id, &entries); + if (start != nullptr) { + assert(entries.length() == entry_count - 1, + "unexpected entries count %d", entries.length()); + StubRoutines::aarch64::_vector_iota_indices[0] = start; + for (int i = 1; i < VECTOR_IOTA_COUNT; i++) { + StubRoutines::aarch64::_vector_iota_indices[i] = entries.at(i - 1); + } + return; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); // B __ emit_data64(0x0706050403020100, relocInfo::none); __ emit_data64(0x0F0E0D0C0B0A0908, relocInfo::none); + entries.append(__ pc()); // H __ emit_data64(0x0003000200010000, relocInfo::none); __ emit_data64(0x0007000600050004, relocInfo::none); + entries.append(__ pc()); // S __ emit_data64(0x0000000100000000, relocInfo::none); __ emit_data64(0x0000000300000002, relocInfo::none); + entries.append(__ pc()); // D __ emit_data64(0x0000000000000000, relocInfo::none); __ emit_data64(0x0000000000000001, relocInfo::none); + entries.append(__ pc()); // S - FP __ emit_data64(0x3F80000000000000, relocInfo::none); // 0.0f, 1.0f __ emit_data64(0x4040000040000000, relocInfo::none); // 2.0f, 3.0f + entries.append(__ pc()); // D - FP __ emit_data64(0x0000000000000000, relocInfo::none); // 0.0d __ emit_data64(0x3FF0000000000000, relocInfo::none); // 1.0d - return start; + + // record the stub entry and end + store_archive_data(stub_id, start, __ pc(), &entries); + + // install the entry addresses in the entry array + assert(entries.length() == entry_count - 1, + "unexpected entries count %d", entries.length()); + StubRoutines::aarch64::_vector_iota_indices[0] = start; + for (int i = 1; i < VECTOR_IOTA_COUNT; i++) { + StubRoutines::aarch64::_vector_iota_indices[i] = entries.at(i - 1); + } } // The inner part of zero_words(). This is the bulk operation, @@ -656,15 +886,21 @@ class StubGenerator: public StubCodeGenerator { // r11 < MacroAssembler::zero_words_block_size. address generate_zero_blocks() { + StubId stub_id = StubId::stubgen_zero_blocks_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); + StubCodeMark mark(this, stub_id); Label done; Label base_aligned; Register base = r10, cnt = r11; - __ align(CodeEntryAlignment); - StubId stub_id = StubId::stubgen_zero_blocks_id; - StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); if (UseBlockZeroing) { int zva_length = VM_Version::zva_length(); @@ -707,6 +943,9 @@ class StubGenerator: public StubCodeGenerator { __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -803,6 +1042,12 @@ class StubGenerator: public StubCodeGenerator { // s and d are adjusted to point to the remaining words to copy // address generate_copy_longs(StubId stub_id, DecoratorSet decorators, Register s, Register d, Register count) { + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } BasicType type; copy_direction direction; @@ -854,7 +1099,7 @@ class StubGenerator: public StubCodeGenerator { StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label unaligned_copy_long; if (AvoidUnalignedAccesses) { @@ -1154,6 +1399,9 @@ class StubGenerator: public StubCodeGenerator { __ ret(lr); } + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -1445,19 +1693,25 @@ class StubGenerator: public StubCodeGenerator { } if (direction == copy_forwards) { if (type != T_OBJECT) { - __ bl(StubRoutines::aarch64::copy_byte_f()); + __ lea(rscratch1, RuntimeAddress(StubRoutines::aarch64::copy_byte_f())); + __ blr(rscratch1); } else if ((decorators & IS_DEST_UNINITIALIZED) != 0) { - __ bl(StubRoutines::aarch64::copy_oop_uninit_f()); + __ lea(rscratch1, RuntimeAddress(StubRoutines::aarch64::copy_oop_uninit_f())); + __ blr(rscratch1); } else { - __ bl(StubRoutines::aarch64::copy_oop_f()); + __ lea(rscratch1, RuntimeAddress(StubRoutines::aarch64::copy_oop_f())); + __ blr(rscratch1); } } else { if (type != T_OBJECT) { - __ bl(StubRoutines::aarch64::copy_byte_b()); + __ lea(rscratch1, RuntimeAddress(StubRoutines::aarch64::copy_byte_b())); + __ blr(rscratch1); } else if ((decorators & IS_DEST_UNINITIALIZED) != 0) { - __ bl(StubRoutines::aarch64::copy_oop_uninit_b()); + __ lea(rscratch1, RuntimeAddress(StubRoutines::aarch64::copy_oop_uninit_b())); + __ blr(rscratch1); } else { - __ bl(StubRoutines::aarch64::copy_oop_b()); + __ lea(rscratch1, RuntimeAddress(StubRoutines::aarch64::copy_oop_b())); + __ blr(rscratch1); } } @@ -1508,8 +1762,8 @@ class StubGenerator: public StubCodeGenerator { // stub_id - is used to name the stub and identify all details of // how to perform the copy. // - // entry - is assigned to the stub's post push entry point unless - // it is null + // nopush_entry - is assigned to the stub's post push entry point + // unless it is null // // Inputs: // c_rarg0 - source array address @@ -1525,8 +1779,6 @@ class StubGenerator: public StubCodeGenerator { // copy method // address generate_disjoint_copy(StubId stub_id, address *nopush_entry) { - Register s = c_rarg0, d = c_rarg1, count = c_rarg2; - RegSet saved_reg = RegSet::of(s, d, count); int size; bool aligned; bool is_oop; @@ -1607,17 +1859,45 @@ class StubGenerator: public StubCodeGenerator { ShouldNotReachHere(); break; } + // all stubs provide a 2nd entry which omits the frame push for + // use when bailing out from a conjoint copy. However we may also + // need some extra addressses for memory access protection. + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 2, "sanity check"); + assert(nopush_entry != nullptr, "all disjoint copy stubs export a nopush entry"); + + bool add_extras = !is_oop && (!aligned || sizeof(jlong) == size); + int extra_count = ((add_extras ? 1 : 0) * UnsafeMemoryAccess::COLUMN_COUNT); + GrowableArray
entries; + GrowableArray
extras; + GrowableArray
*extras_ptr = (extra_count > 0 ? &extras : nullptr); + address start = load_archive_data(stub_id, &entries, extras_ptr); + if (start != nullptr) { + assert(entries.length() == entry_count - 1, + "unexpected entries count %d", entries.length()); + *nopush_entry = entries.at(0); + assert(extras.length() == extra_count, + "unexpected extra count %d", extras.length()); + if (add_extras) { + // register one handler at offset 0 + register_unsafe_access_handlers(extras, 0, 1); + } + return start; + } + + Register s = c_rarg0, d = c_rarg1, count = c_rarg2; + RegSet saved_reg = RegSet::of(s, d, count); __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); - if (nopush_entry != nullptr) { - *nopush_entry = __ pc(); - // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) - BLOCK_COMMENT("Entry:"); - } + *nopush_entry = __ pc(); + entries.append(*nopush_entry); + + // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) + BLOCK_COMMENT("Post-Push Entry:"); DecoratorSet decorators = IN_HEAP | IS_ARRAY | ARRAYCOPY_DISJOINT; if (dest_uninitialized) { @@ -1636,8 +1916,7 @@ class StubGenerator: public StubCodeGenerator { } { // UnsafeMemoryAccess page error: continue after unsafe access - bool add_entry = !is_oop && (!aligned || sizeof(jlong) == size); - UnsafeMemoryAccessMark umam(this, add_entry, true); + UnsafeMemoryAccessMark umam(this, add_extras, true); copy_memory(decorators, is_oop ? T_OBJECT : T_BYTE, aligned, s, d, count, size); } @@ -1652,6 +1931,20 @@ class StubGenerator: public StubCodeGenerator { __ leave(); __ mov(r0, zr); // return 0 __ ret(lr); + + address end = __ pc(); + + if (add_extras) { + // retrieve the registered handler addresses + retrieve_unsafe_access_handlers(start, end, extras); + assert(extras.length() == extra_count + , "incorrect handlers count %d", extras.length()); + } + + // record the stub entry and end plus the no_push entry and any + // extra handler addresses + store_archive_data(stub_id, start, end, &entries, extras_ptr); + return start; } @@ -1663,8 +1956,8 @@ class StubGenerator: public StubCodeGenerator { // corresponding disjoint copy routine which can be // jumped to if the ranges do not actually overlap // - // entry - is assigned to the stub's post push entry point unless - // it is null + // nopush_entry - is assigned to the stub's post push entry point + // unless it is null // // // Inputs: @@ -1681,8 +1974,6 @@ class StubGenerator: public StubCodeGenerator { // used by some other conjoint copy method // address generate_conjoint_copy(StubId stub_id, address nooverlap_target, address *nopush_entry) { - Register s = c_rarg0, d = c_rarg1, count = c_rarg2; - RegSet saved_regs = RegSet::of(s, d, count); int size; bool aligned; bool is_oop; @@ -1762,15 +2053,47 @@ class StubGenerator: public StubCodeGenerator { default: ShouldNotReachHere(); } + // only some conjoint stubs generate a 2nd entry + int entry_count = StubInfo::entry_count(stub_id); + int expected_entry_count = (nopush_entry == nullptr ? 1 : 2); + assert(entry_count == expected_entry_count, + "expected entry count %d does not match declared entry count %d for stub %s", + expected_entry_count, entry_count, StubInfo::name(stub_id)); + // We need to protect memory accesses in certain cases + bool add_extras = !is_oop && (!aligned || sizeof(jlong) == size); + int extra_count = ((add_extras ? 1 : 0) * UnsafeMemoryAccess::COLUMN_COUNT); + GrowableArray
entries; + GrowableArray
extras; + GrowableArray
*entries_ptr = (nopush_entry != nullptr ? &entries : nullptr); + GrowableArray
*extras_ptr = (extra_count > 0 ? &extras : nullptr); + address start = load_archive_data(stub_id, entries_ptr, extras_ptr); + if (start != nullptr) { + assert(entries.length() == expected_entry_count - 1, + "unexpected entries count %d", entries.length()); + assert(extras.length() == extra_count, + "unexpected extra count %d", extras.length()); + if (nopush_entry != nullptr) { + *nopush_entry = entries.at(0); + } + if (add_extras) { + // register one handler at offset 0 + register_unsafe_access_handlers(extras, 0, 1); + } + return start; + } + + Register s = c_rarg0, d = c_rarg1, count = c_rarg2; + RegSet saved_regs = RegSet::of(s, d, count); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); if (nopush_entry != nullptr) { *nopush_entry = __ pc(); + entries.append(*nopush_entry); // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) - BLOCK_COMMENT("Entry:"); + BLOCK_COMMENT("Post-Push Entry:"); } // use fwd copy when (d-s) above_equal (count*size) @@ -1798,8 +2121,7 @@ class StubGenerator: public StubCodeGenerator { } { // UnsafeMemoryAccess page error: continue after unsafe access - bool add_entry = !is_oop && (!aligned || sizeof(jlong) == size); - UnsafeMemoryAccessMark umam(this, add_entry, true); + UnsafeMemoryAccessMark umam(this, add_extras, true); copy_memory(decorators, is_oop ? T_OBJECT : T_BYTE, aligned, s, d, count, -size); } if (is_oop) { @@ -1811,6 +2133,23 @@ class StubGenerator: public StubCodeGenerator { __ leave(); __ mov(r0, zr); // return 0 __ ret(lr); + + assert(entries.length() == expected_entry_count - 1, + "unexpected entries count %d", entries.length()); + + address end = __ pc(); + + if (add_extras) { + // retrieve the registered handler addresses + retrieve_unsafe_access_handlers(start, end, extras); + assert(extras.length() == extra_count, + "incorrect handlers count %d", extras.length()); + } + + // record the stub entry and end plus any no_push entry and/or + // extra handler addresses + store_archive_data(stub_id, start, end, entries_ptr, extras_ptr); + return start; } @@ -1864,6 +2203,27 @@ class StubGenerator: public StubCodeGenerator { ShouldNotReachHere(); } + // The normal stub provides a 2nd entry which omits the frame push + // for use when bailing out from a disjoint copy. + // Only some conjoint stubs generate a 2nd entry + int entry_count = StubInfo::entry_count(stub_id); + int expected_entry_count = (nopush_entry == nullptr ? 1 : 2); + GrowableArray
entries; + GrowableArray
*entries_ptr = (expected_entry_count == 1 ? nullptr : &entries); + assert(entry_count == expected_entry_count, + "expected entry count %d does not match declared entry count %d for stub %s", + expected_entry_count, entry_count, StubInfo::name(stub_id)); + address start = load_archive_data(stub_id, entries_ptr); + if (start != nullptr) { + assert(entries.length() + 1 == expected_entry_count, + "expected entry count %d does not match return entry count %d for stub %s", + expected_entry_count, entries.length() + 1, StubInfo::name(stub_id)); + if (nopush_entry != nullptr) { + *nopush_entry = entries.at(0); + } + return start; + } + Label L_load_element, L_store_element, L_do_card_marks, L_done, L_done_pop; // Input registers (after setup_arg_regs) @@ -1896,7 +2256,7 @@ class StubGenerator: public StubCodeGenerator { __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); // required for proper stackwalking of RuntimeStub frame @@ -1913,6 +2273,7 @@ class StubGenerator: public StubCodeGenerator { // Caller of this entry point must set up the argument registers. if (nopush_entry != nullptr) { *nopush_entry = __ pc(); + entries.append(*nopush_entry); BLOCK_COMMENT("Entry:"); } @@ -2010,6 +2371,8 @@ class StubGenerator: public StubCodeGenerator { __ leave(); __ ret(lr); + // record the stub entry and end plus any no_push entry + store_archive_data(stub_id, start, __ pc() , entries_ptr); return start; } @@ -2072,13 +2435,18 @@ class StubGenerator: public StubCodeGenerator { address int_copy_entry, address long_copy_entry) { StubId stub_id = StubId::stubgen_unsafe_arraycopy_id; - + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } Label L_long_aligned, L_int_aligned, L_short_aligned; Register s = c_rarg0, d = c_rarg1, count = c_rarg2; __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); // required for proper stackwalking of RuntimeStub frame // bump this on entry, not on exit: @@ -2104,6 +2472,9 @@ class StubGenerator: public StubCodeGenerator { __ lsr(count, count, LogBytesPerLong); // size => long_count __ b(RuntimeAddress(long_copy_entry)); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -2125,7 +2496,12 @@ class StubGenerator: public StubCodeGenerator { address int_copy_entry, address oop_copy_entry, address long_copy_entry, address checkcast_copy_entry) { StubId stub_id = StubId::stubgen_generic_arraycopy_id; - + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } Label L_failed, L_objArray; Label L_copy_bytes, L_copy_shorts, L_copy_ints, L_copy_longs; @@ -2144,7 +2520,7 @@ class StubGenerator: public StubCodeGenerator { StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); // required for proper stackwalking of RuntimeStub frame @@ -2383,6 +2759,9 @@ class StubGenerator: public StubCodeGenerator { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -2427,10 +2806,15 @@ class StubGenerator: public StubCodeGenerator { default: ShouldNotReachHere(); }; - + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); BLOCK_COMMENT("Entry:"); @@ -2563,15 +2947,32 @@ class StubGenerator: public StubCodeGenerator { __ bind(L_exit2); __ leave(); __ ret(lr); + + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address generate_unsafecopy_common_error_exit() { - address start_pc = __ pc(); + StubId stub_id = StubId::stubgen_unsafecopy_common_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); + StubCodeMark mark(this, stub_id); + start = __ pc(); __ leave(); __ mov(r0, 0); __ ret(lr); - return start_pc; + + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + + return start; } // @@ -2589,13 +2990,28 @@ class StubGenerator: public StubCodeGenerator { // c_rarg2 - byte value // address generate_unsafe_setmemory() { + StubId stub_id = StubId::stubgen_unsafe_setmemory_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + // we expect one set of extra unsafememory access handler entries + GrowableArray
extras; + int extra_count = 1 * UnsafeMemoryAccess::COLUMN_COUNT; + address start = load_archive_data(stub_id, nullptr, &extras); + if (start != nullptr) { + assert(extras.length() == extra_count, + "unexpected extra entry count %d", extras.length()); + register_unsafe_access_handlers(extras, 0, 1); + return start; + } + __ align(CodeEntryAlignment); - StubCodeMark mark(this, StubId::stubgen_unsafe_setmemory_id); - address start = __ pc(); + StubCodeMark mark(this, stub_id); + start = __ pc(); Register dest = c_rarg0, count = c_rarg1, value = c_rarg2; Label tail; + { UnsafeMemoryAccessMark umam(this, true, false); __ enter(); // required for proper stackwalking of RuntimeStub frame @@ -2679,6 +3095,17 @@ class StubGenerator: public StubCodeGenerator { __ bind(finished); __ leave(); __ ret(lr); + // have to exit the block and destroy the UnsafeMemoryAccessMark + // in order to retrieve the handler end address + } + + // install saved handler addresses in extras + address end = __ pc(); + retrieve_unsafe_access_handlers(start, end, extras); + assert(extras.length() == extra_count, + "incorrect handlers count %d", extras.length()); + // record the stub entry and end plus the extras + store_archive_data(stub_id, start, end, nullptr, &extras); return start; } @@ -2686,33 +3113,45 @@ class StubGenerator: public StubCodeGenerator { address generate_data_cache_writeback() { const Register line = c_rarg0; // address of line to write back - __ align(CodeEntryAlignment); - StubId stub_id = StubId::stubgen_data_cache_writeback_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); __ cache_wb(Address(line, 0)); __ leave(); __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address generate_data_cache_writeback_sync() { - const Register is_pre = c_rarg0; // pre or post sync - - __ align(CodeEntryAlignment); - StubId stub_id = StubId::stubgen_data_cache_writeback_sync_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + const Register is_pre = c_rarg0; // pre or post sync + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); // pre wbsync is a no-op // post wbsync translates to an sfence Label skip; - address start = __ pc(); + start = __ pc(); __ enter(); __ cbnz(is_pre, skip); __ cache_wbsync(false); @@ -2720,6 +3159,9 @@ class StubGenerator: public StubCodeGenerator { __ leave(); __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -2882,8 +3324,15 @@ class StubGenerator: public StubCodeGenerator { // c_rarg2 - sessionKe (key) in little endian int array // address generate_aescrypt_encryptBlock() { - __ align(CodeEntryAlignment); + assert(UseAES, "need AES cryptographic extension support"); StubId stub_id = StubId::stubgen_aescrypt_encryptBlock_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); const Register from = c_rarg0; // source array address @@ -2891,7 +3340,7 @@ class StubGenerator: public StubCodeGenerator { const Register key = c_rarg2; // key array address const Register keylen = rscratch1; - address start = __ pc(); + start = __ pc(); __ enter(); __ ldrw(keylen, Address(key, arrayOopDesc::length_offset_in_bytes() - arrayOopDesc::base_offset_in_bytes(T_INT))); @@ -2904,6 +3353,9 @@ class StubGenerator: public StubCodeGenerator { __ leave(); __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -2916,8 +3368,14 @@ class StubGenerator: public StubCodeGenerator { // address generate_aescrypt_decryptBlock() { assert(UseAES, "need AES cryptographic extension support"); - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_aescrypt_decryptBlock_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); Label L_doLast; @@ -2926,7 +3384,7 @@ class StubGenerator: public StubCodeGenerator { const Register key = c_rarg2; // key array address const Register keylen = rscratch1; - address start = __ pc(); + start = __ pc(); __ enter(); // required for proper stackwalking of RuntimeStub frame __ ldrw(keylen, Address(key, arrayOopDesc::length_offset_in_bytes() - arrayOopDesc::base_offset_in_bytes(T_INT))); @@ -2938,6 +3396,9 @@ class StubGenerator: public StubCodeGenerator { __ leave(); __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -2955,8 +3416,14 @@ class StubGenerator: public StubCodeGenerator { // address generate_cipherBlockChaining_encryptAESCrypt() { assert(UseAES, "need AES cryptographic extension support"); - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_cipherBlockChaining_encryptAESCrypt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); Label L_loadkeys_44, L_loadkeys_52, L_aes_loop, L_rounds_44, L_rounds_52; @@ -2969,7 +3436,7 @@ class StubGenerator: public StubCodeGenerator { const Register len_reg = c_rarg4; // src len (must be multiple of blocksize 16) const Register keylen = rscratch1; - address start = __ pc(); + start = __ pc(); __ enter(); @@ -3043,6 +3510,9 @@ class StubGenerator: public StubCodeGenerator { __ leave(); __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -3060,8 +3530,14 @@ class StubGenerator: public StubCodeGenerator { // address generate_cipherBlockChaining_decryptAESCrypt() { assert(UseAES, "need AES cryptographic extension support"); - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_cipherBlockChaining_decryptAESCrypt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); Label L_loadkeys_44, L_loadkeys_52, L_aes_loop, L_rounds_44, L_rounds_52; @@ -3074,7 +3550,7 @@ class StubGenerator: public StubCodeGenerator { const Register len_reg = c_rarg4; // src len (must be multiple of blocksize 16) const Register keylen = rscratch1; - address start = __ pc(); + start = __ pc(); __ enter(); @@ -3152,6 +3628,9 @@ class StubGenerator: public StubCodeGenerator { __ leave(); __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -3188,6 +3667,13 @@ class StubGenerator: public StubCodeGenerator { // r0 - input length // address generate_counterMode_AESCrypt() { + StubId stub_id = StubId::stubgen_counterMode_AESCrypt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } const Register in = c_rarg0; const Register out = c_rarg1; const Register key = c_rarg2; @@ -3248,9 +3734,8 @@ class StubGenerator: public StubCodeGenerator { // Wide bulk encryption of whole blocks. __ align(CodeEntryAlignment); - StubId stub_id = StubId::stubgen_counterMode_AESCrypt_id; StubCodeMark mark(this, stub_id); - const address start = __ pc(); + start = __ pc(); __ enter(); Label DONE, CTR_large_block, large_block_return; @@ -3435,6 +3920,9 @@ class StubGenerator: public StubCodeGenerator { __ strw(used, Address(used_ptr)); __ b(large_block_return); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -3451,11 +3939,16 @@ class StubGenerator: public StubCodeGenerator { // return - number of processed bytes address generate_galoisCounterMode_AESCrypt() { Label ghash_polynomial; // local data generated after code - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_galoisCounterMode_AESCrypt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); const Register in = c_rarg0; @@ -3567,6 +4060,9 @@ class StubGenerator: public StubCodeGenerator { // 128-bit vector __ emit_int64(0x87); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -3685,10 +4181,16 @@ class StubGenerator: public StubCodeGenerator { default: ShouldNotReachHere(); } + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Register buf = c_rarg0; Register state = c_rarg1; @@ -3815,6 +4317,9 @@ class StubGenerator: public StubCodeGenerator { __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -3838,11 +4343,16 @@ class StubGenerator: public StubCodeGenerator { default: ShouldNotReachHere(); } - + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Register buf = c_rarg0; Register state = c_rarg1; @@ -3919,6 +4429,9 @@ class StubGenerator: public StubCodeGenerator { __ emit_int32(0x8f1bbcdc); __ emit_int32(0xca62c1d6); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -3943,30 +4456,15 @@ class StubGenerator: public StubCodeGenerator { default: ShouldNotReachHere(); } - - static const uint32_t round_consts[64] = { - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, - 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, - 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, - 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, - 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, - 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, - 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, - 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, - 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, - 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, - 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, - 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, - 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, - 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, - }; - + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); - StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Register buf = c_rarg0; Register state = c_rarg1; @@ -3987,7 +4485,7 @@ class StubGenerator: public StubCodeGenerator { // t1 == v7 // load 16 keys to v16..v31 - __ lea(rscratch1, ExternalAddress((address)round_consts)); + __ lea(rscratch1, ExternalAddress((address)_sha256_round_consts)); __ ld1(v16, v17, v18, v19, __ T4S, __ post(rscratch1, 64)); __ ld1(v20, v21, v22, v23, __ T4S, __ post(rscratch1, 64)); __ ld1(v24, v25, v26, v27, __ T4S, __ post(rscratch1, 64)); @@ -4048,6 +4546,9 @@ class StubGenerator: public StubCodeGenerator { __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -4099,41 +4600,15 @@ class StubGenerator: public StubCodeGenerator { default: ShouldNotReachHere(); } - - static const uint64_t round_consts[80] = { - 0x428A2F98D728AE22L, 0x7137449123EF65CDL, 0xB5C0FBCFEC4D3B2FL, - 0xE9B5DBA58189DBBCL, 0x3956C25BF348B538L, 0x59F111F1B605D019L, - 0x923F82A4AF194F9BL, 0xAB1C5ED5DA6D8118L, 0xD807AA98A3030242L, - 0x12835B0145706FBEL, 0x243185BE4EE4B28CL, 0x550C7DC3D5FFB4E2L, - 0x72BE5D74F27B896FL, 0x80DEB1FE3B1696B1L, 0x9BDC06A725C71235L, - 0xC19BF174CF692694L, 0xE49B69C19EF14AD2L, 0xEFBE4786384F25E3L, - 0x0FC19DC68B8CD5B5L, 0x240CA1CC77AC9C65L, 0x2DE92C6F592B0275L, - 0x4A7484AA6EA6E483L, 0x5CB0A9DCBD41FBD4L, 0x76F988DA831153B5L, - 0x983E5152EE66DFABL, 0xA831C66D2DB43210L, 0xB00327C898FB213FL, - 0xBF597FC7BEEF0EE4L, 0xC6E00BF33DA88FC2L, 0xD5A79147930AA725L, - 0x06CA6351E003826FL, 0x142929670A0E6E70L, 0x27B70A8546D22FFCL, - 0x2E1B21385C26C926L, 0x4D2C6DFC5AC42AEDL, 0x53380D139D95B3DFL, - 0x650A73548BAF63DEL, 0x766A0ABB3C77B2A8L, 0x81C2C92E47EDAEE6L, - 0x92722C851482353BL, 0xA2BFE8A14CF10364L, 0xA81A664BBC423001L, - 0xC24B8B70D0F89791L, 0xC76C51A30654BE30L, 0xD192E819D6EF5218L, - 0xD69906245565A910L, 0xF40E35855771202AL, 0x106AA07032BBD1B8L, - 0x19A4C116B8D2D0C8L, 0x1E376C085141AB53L, 0x2748774CDF8EEB99L, - 0x34B0BCB5E19B48A8L, 0x391C0CB3C5C95A63L, 0x4ED8AA4AE3418ACBL, - 0x5B9CCA4F7763E373L, 0x682E6FF3D6B2B8A3L, 0x748F82EE5DEFB2FCL, - 0x78A5636F43172F60L, 0x84C87814A1F0AB72L, 0x8CC702081A6439ECL, - 0x90BEFFFA23631E28L, 0xA4506CEBDE82BDE9L, 0xBEF9A3F7B2C67915L, - 0xC67178F2E372532BL, 0xCA273ECEEA26619CL, 0xD186B8C721C0C207L, - 0xEADA7DD6CDE0EB1EL, 0xF57D4F7FEE6ED178L, 0x06F067AA72176FBAL, - 0x0A637DC5A2C898A6L, 0x113F9804BEF90DAEL, 0x1B710B35131C471BL, - 0x28DB77F523047D84L, 0x32CAAB7B40C72493L, 0x3C9EBE0A15C9BEBCL, - 0x431D67C49C100D4CL, 0x4CC5D4BECB3E42B6L, 0x597F299CFC657E2AL, - 0x5FCB6FAB3AD6FAECL, 0x6C44198C4A475817L - }; - + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); - StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Register buf = c_rarg0; Register state = c_rarg1; @@ -4151,7 +4626,7 @@ class StubGenerator: public StubCodeGenerator { __ ld1(v8, v9, v10, v11, __ T2D, state); // load first 4 round constants - __ lea(rscratch1, ExternalAddress((address)round_consts)); + __ lea(rscratch1, ExternalAddress((address)_sha512_round_consts)); __ ld1(v20, v21, v22, v23, __ T2D, __ post(rscratch1, 64)); __ BIND(sha512_loop); @@ -4236,6 +4711,9 @@ class StubGenerator: public StubCodeGenerator { __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -4349,22 +4827,15 @@ class StubGenerator: public StubCodeGenerator { default: ShouldNotReachHere(); } - - static const uint64_t round_consts[24] = { - 0x0000000000000001L, 0x0000000000008082L, 0x800000000000808AL, - 0x8000000080008000L, 0x000000000000808BL, 0x0000000080000001L, - 0x8000000080008081L, 0x8000000000008009L, 0x000000000000008AL, - 0x0000000000000088L, 0x0000000080008009L, 0x000000008000000AL, - 0x000000008000808BL, 0x800000000000008BL, 0x8000000000008089L, - 0x8000000000008003L, 0x8000000000008002L, 0x8000000000000080L, - 0x000000000000800AL, 0x800000008000000AL, 0x8000000080008081L, - 0x8000000000008080L, 0x0000000080000001L, 0x8000000080008008L - }; - + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); - StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Register buf = c_rarg0; Register state = c_rarg1; @@ -4396,7 +4867,7 @@ class StubGenerator: public StubCodeGenerator { __ movw(rscratch2, 24); // load round_constants base - __ lea(rscratch1, ExternalAddress((address) round_consts)); + __ lea(rscratch1, ExternalAddress((address) _sha3_round_consts)); // load input __ ld1(v25, v26, v27, v28, __ T8B, __ post(buf, 32)); @@ -4488,6 +4959,9 @@ class StubGenerator: public StubCodeGenerator { __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -4495,22 +4969,18 @@ class StubGenerator: public StubCodeGenerator { // c_rarg0 - long[] state0 // c_rarg1 - long[] state1 address generate_double_keccak() { - static const uint64_t round_consts[24] = { - 0x0000000000000001L, 0x0000000000008082L, 0x800000000000808AL, - 0x8000000080008000L, 0x000000000000808BL, 0x0000000080000001L, - 0x8000000080008081L, 0x8000000000008009L, 0x000000000000008AL, - 0x0000000000000088L, 0x0000000080008009L, 0x000000008000000AL, - 0x000000008000808BL, 0x800000000000008BL, 0x8000000000008089L, - 0x8000000000008003L, 0x8000000000008002L, 0x8000000000000080L, - 0x000000000000800AL, 0x800000008000000AL, 0x8000000080008081L, - 0x8000000000008080L, 0x0000000080000001L, 0x8000000080008008L - }; - + StubId stub_id = StubId::stubgen_double_keccak_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } // Implements the double_keccak() method of the // sun.secyrity.provider.SHA3Parallel class __ align(CodeEntryAlignment); - StubCodeMark mark(this, "StubRoutines", "double_keccak"); - address start = __ pc(); + StubCodeMark mark(this, stub_id); + start = __ pc(); __ enter(); Register state0 = c_rarg0; @@ -4546,7 +5016,7 @@ class StubGenerator: public StubCodeGenerator { __ movw(rscratch2, 24); // load round_constants base - __ lea(rscratch1, ExternalAddress((address) round_consts)); + __ lea(rscratch1, ExternalAddress((address) _double_keccak_round_consts)); __ BIND(rounds24_loop); __ subw(rscratch2, rscratch2, 1); @@ -4578,6 +5048,9 @@ class StubGenerator: public StubCodeGenerator { __ mov(r0, zr); // return 0 __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -4611,11 +5084,17 @@ class StubGenerator: public StubCodeGenerator { // vectors write their first lane back to the keystream buffer, followed // by the second lane from all vectors and so on. address generate_chacha20Block_blockpar() { + StubId stub_id = StubId::stubgen_chacha20Block_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } Label L_twoRounds, L_cc20_const; __ align(CodeEntryAlignment); - StubId stub_id = StubId::stubgen_chacha20Block_id; StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); int i, j; @@ -4770,6 +5249,9 @@ class StubGenerator: public StubCodeGenerator { __ emit_int64(0x0605040702010003UL); __ emit_int64(0x0E0D0C0F0A09080BUL); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -5258,11 +5740,16 @@ class StubGenerator: public StubCodeGenerator { // coeffs (short[256]) = c_rarg0 // ntt_zetas (short[256]) = c_rarg1 address generate_kyberNtt() { - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_kyberNtt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); const Register coeffs = c_rarg0; @@ -5486,6 +5973,9 @@ class StubGenerator: public StubCodeGenerator { __ mov(r0, zr); // return 0 __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -5496,11 +5986,16 @@ class StubGenerator: public StubCodeGenerator { // coeffs (short[256]) = c_rarg0 // ntt_zetas (short[256]) = c_rarg1 address generate_kyberInverseNtt() { - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_kyberInverseNtt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); const Register coeffs = c_rarg0; @@ -5770,6 +6265,9 @@ class StubGenerator: public StubCodeGenerator { __ mov(r0, zr); // return 0 __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -5783,11 +6281,16 @@ class StubGenerator: public StubCodeGenerator { // nttb (short[256]) = c_rarg2 // zetas (short[128]) = c_rarg3 address generate_kyberNttMult() { - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_kyberNttMult_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); const Register result = c_rarg0; @@ -5889,6 +6392,9 @@ class StubGenerator: public StubCodeGenerator { __ mov(r0, zr); // return 0 __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -5900,11 +6406,16 @@ class StubGenerator: public StubCodeGenerator { // a (short[256]) = c_rarg1 // b (short[256]) = c_rarg2 address generate_kyberAddPoly_2() { - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_kyberAddPoly_2_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); const Register result = c_rarg0; @@ -5973,6 +6484,9 @@ class StubGenerator: public StubCodeGenerator { __ mov(r0, zr); // return 0 __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -5985,11 +6499,16 @@ class StubGenerator: public StubCodeGenerator { // b (short[256]) = c_rarg2 // c (short[256]) = c_rarg3 address generate_kyberAddPoly_3() { - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_kyberAddPoly_3_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); const Register result = c_rarg0; @@ -6072,6 +6591,9 @@ class StubGenerator: public StubCodeGenerator { __ mov(r0, zr); // return 0 __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -6092,12 +6614,18 @@ class StubGenerator: public StubCodeGenerator { // parsed (short[]) = c_rarg2 // parsedLength = c_rarg3 address generate_kyber12To16() { + StubId stub_id = StubId::stubgen_kyber12To16_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } Label L_F00, L_loop; __ align(CodeEntryAlignment); - StubId stub_id = StubId::stubgen_kyber12To16_id; StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); const Register condensed = c_rarg0; @@ -6225,6 +6753,9 @@ class StubGenerator: public StubCodeGenerator { __ emit_int64(0x0f000f000f000f00); __ emit_int64(0x0f000f000f000f00); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -6234,11 +6765,16 @@ class StubGenerator: public StubCodeGenerator { // // coeffs (short[256]) = c_rarg0 address generate_kyberBarrettReduce() { - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_kyberBarrettReduce_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); const Register coeffs = c_rarg0; @@ -6318,6 +6854,9 @@ class StubGenerator: public StubCodeGenerator { __ mov(r0, zr); // return 0 __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -6481,11 +7020,16 @@ class StubGenerator: public StubCodeGenerator { // coeffs (int[256]) = c_rarg0 // zetas (int[256]) = c_rarg1 address generate_dilithiumAlmostNtt() { - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_dilithiumAlmostNtt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); const Register coeffs = c_rarg0; @@ -6596,6 +7140,9 @@ class StubGenerator: public StubCodeGenerator { __ mov(r0, zr); // return 0 __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -6688,11 +7235,16 @@ class StubGenerator: public StubCodeGenerator { // coeffs (int[256]) = c_rarg0 // zetas (int[256]) = c_rarg1 address generate_dilithiumAlmostInverseNtt() { - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_dilithiumAlmostInverseNtt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); const Register coeffs = c_rarg0; @@ -6788,6 +7340,9 @@ class StubGenerator: public StubCodeGenerator { __ mov(r0, zr); // return 0 __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -6801,11 +7356,16 @@ class StubGenerator: public StubCodeGenerator { // poly1 (int[256]) = c_rarg1 // poly2 (int[256]) = c_rarg2 address generate_dilithiumNttMult() { - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_dilithiumNttMult_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); Label L_loop; @@ -6854,6 +7414,9 @@ class StubGenerator: public StubCodeGenerator { __ mov(r0, zr); // return 0 __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -6865,11 +7428,16 @@ class StubGenerator: public StubCodeGenerator { // coeffs (int[256]) = c_rarg0 // constant (int) = c_rarg1 address generate_dilithiumMontMulByConstant() { - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_dilithiumMontMulByConstant_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); Label L_loop; @@ -6915,6 +7483,9 @@ class StubGenerator: public StubCodeGenerator { __ mov(r0, zr); // return 0 __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -6929,11 +7500,16 @@ class StubGenerator: public StubCodeGenerator { // twoGamma2 (int) = c_rarg3 // multiplier (int) = c_rarg4 address generate_dilithiumDecomposePoly() { - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_dilithiumDecomposePoly_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label L_loop; const Register input = c_rarg0; @@ -7073,6 +7649,9 @@ class StubGenerator: public StubCodeGenerator { __ mov(r0, zr); // return 0 __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -7212,21 +7791,15 @@ class StubGenerator: public StubCodeGenerator { default: ShouldNotReachHere(); } - - static const uint64_t round_consts[24] = { - 0x0000000000000001L, 0x0000000000008082L, 0x800000000000808AL, - 0x8000000080008000L, 0x000000000000808BL, 0x0000000080000001L, - 0x8000000080008081L, 0x8000000000008009L, 0x000000000000008AL, - 0x0000000000000088L, 0x0000000080008009L, 0x000000008000000AL, - 0x000000008000808BL, 0x800000000000008BL, 0x8000000000008089L, - 0x8000000000008003L, 0x8000000000008002L, 0x8000000000000080L, - 0x000000000000800AL, 0x800000008000000AL, 0x8000000080008081L, - 0x8000000000008080L, 0x0000000080000001L, 0x8000000080008008L - }; - + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Register buf = c_rarg0; Register state = c_rarg1; @@ -7378,7 +7951,7 @@ class StubGenerator: public StubCodeGenerator { __ fmovs(v1, 1.0); // exact representation __ str(buf, Address(sp, 16)); - __ lea(tmp3, ExternalAddress((address) round_consts)); + __ lea(tmp3, ExternalAddress((address) _sha3_round_consts)); __ BIND(loop_body); keccak_round_gpr(can_use_fp, can_use_r18, tmp3, @@ -7433,6 +8006,9 @@ class StubGenerator: public StubCodeGenerator { __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -7449,12 +8025,17 @@ class StubGenerator: public StubCodeGenerator { */ address generate_updateBytesCRC32() { assert(UseCRC32Intrinsics, "what are we doing here?"); - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_updateBytesCRC32_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); const Register crc = c_rarg0; // crc const Register buf = c_rarg1; // source java byte array address @@ -7474,6 +8055,9 @@ class StubGenerator: public StubCodeGenerator { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -7491,12 +8075,17 @@ class StubGenerator: public StubCodeGenerator { */ address generate_updateBytesCRC32C() { assert(UseCRC32CIntrinsics, "what are we doing here?"); - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_updateBytesCRC32C_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); const Register crc = c_rarg0; // crc const Register buf = c_rarg1; // source java byte array address @@ -7516,6 +8105,9 @@ class StubGenerator: public StubCodeGenerator { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -7531,10 +8123,16 @@ class StubGenerator: public StubCodeGenerator { * c_rarg0 - int adler result */ address generate_updateBytesAdler32() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_updateBytesAdler32_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label L_simple_by1_loop, L_nmax, L_nmax_loop, L_by16, L_by16_loop, L_by1_loop, L_do_mod, L_combine, L_by1; @@ -7702,6 +8300,9 @@ class StubGenerator: public StubCodeGenerator { __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -7753,11 +8354,17 @@ class StubGenerator: public StubCodeGenerator { * c_rarg4 - z address */ address generate_multiplyToLen() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_multiplyToLen_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); const Register x = r0; const Register xlen = r1; const Register y = r2; @@ -7779,6 +8386,9 @@ class StubGenerator: public StubCodeGenerator { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -7786,10 +8396,16 @@ class StubGenerator: public StubCodeGenerator { // squareToLen algorithm for sizes 1..127 described in java code works // faster than multiply_to_len on some CPUs and slower on others, but // multiply_to_len shows a bit better overall results - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_squareToLen_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); const Register x = r0; const Register xlen = r1; @@ -7816,15 +8432,25 @@ class StubGenerator: public StubCodeGenerator { __ pop(spilled_regs, sp); __ leave(); __ ret(lr); + + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address generate_mulAdd() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_mulAdd_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); const Register out = r0; const Register in = r1; @@ -7838,6 +8464,9 @@ class StubGenerator: public StubCodeGenerator { __ leave(); __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -7851,10 +8480,16 @@ class StubGenerator: public StubCodeGenerator { // c_rarg4 - numIter // address generate_bigIntegerRightShift() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_bigIntegerRightShiftWorker_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label ShiftSIMDLoop, ShiftTwoLoop, ShiftThree, ShiftTwo, ShiftOne, Exit; @@ -7961,6 +8596,9 @@ class StubGenerator: public StubCodeGenerator { __ BIND(Exit); __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -7974,10 +8612,16 @@ class StubGenerator: public StubCodeGenerator { // c_rarg4 - numIter // address generate_bigIntegerLeftShift() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_bigIntegerLeftShiftWorker_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label ShiftSIMDLoop, ShiftTwoLoop, ShiftThree, ShiftTwo, ShiftOne, Exit; @@ -8072,10 +8716,25 @@ class StubGenerator: public StubCodeGenerator { __ BIND(Exit); __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address generate_count_positives(address &count_positives_long) { + StubId stub_id = StubId::stubgen_count_positives_id; + GrowableArray
entries; + int entry_count = StubInfo::entry_count(stub_id); + // We have an extra entry for count_positives_long. + assert(entry_count == 2, "sanity check"); + address start = load_archive_data(stub_id, &entries); + if (start != nullptr) { + assert(entries.length() == 1, + "unexpected extra entry count %d", entries.length()); + count_positives_long = entries.at(0); + return start; + } const u1 large_loop_size = 64; const uint64_t UPPER_BIT_MASK=0x8080808080808080; int dcache_line = VM_Version::dcache_line_size(); @@ -8083,8 +8742,6 @@ class StubGenerator: public StubCodeGenerator { Register ary1 = r1, len = r2, result = r0; __ align(CodeEntryAlignment); - - StubId stub_id = StubId::stubgen_count_positives_id; StubCodeMark mark(this, stub_id); address entry = __ pc(); @@ -8127,6 +8784,7 @@ class StubGenerator: public StubCodeGenerator { const RegSet spilled_regs = RegSet::range(tmp1, tmp5) + tmp6; count_positives_long = __ pc(); // 2nd entry point + entries.append(count_positives_long); __ enter(); @@ -8241,6 +8899,9 @@ class StubGenerator: public StubCodeGenerator { __ sub(result, result, len); __ ret(lr); + // record the stub entry and end plus the extra entry + store_archive_data(stub_id, entry, __ pc(), &entries); + return entry; } @@ -8331,6 +8992,13 @@ class StubGenerator: public StubCodeGenerator { // r3-r5 are reserved temporary registers // Clobbers: v0-v7 when UseSIMDForArrayEquals, rscratch1, rscratch2 address generate_large_array_equals() { + StubId stub_id = StubId::stubgen_large_array_equals_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } Register a1 = r1, a2 = r2, result = r0, cnt1 = r10, tmp1 = rscratch1, tmp2 = rscratch2, tmp3 = r3, tmp4 = r4, tmp5 = r5, tmp6 = r11, tmp7 = r12, tmp8 = r13; @@ -8346,7 +9014,6 @@ class StubGenerator: public StubCodeGenerator { __ align(CodeEntryAlignment); - StubId stub_id = StubId::stubgen_large_array_equals_id; StubCodeMark mark(this, stub_id); address entry = __ pc(); @@ -8421,6 +9088,10 @@ class StubGenerator: public StubCodeGenerator { __ bind(NOT_EQUAL_NO_POP); __ leave(); __ ret(lr); + + // record the stub entry and end + store_archive_data(stub_id, entry, __ pc()); + return entry; } @@ -8429,6 +9100,33 @@ class StubGenerator: public StubCodeGenerator { // cnt = r2 - elements count // Clobbers: v0-v13, rscratch1, rscratch2 address generate_large_arrays_hashcode(BasicType eltype) { + StubId stub_id; + switch (eltype) { + case T_BOOLEAN: + stub_id = StubId::stubgen_large_arrays_hashcode_boolean_id; + break; + case T_BYTE: + stub_id = StubId::stubgen_large_arrays_hashcode_byte_id; + break; + case T_CHAR: + stub_id = StubId::stubgen_large_arrays_hashcode_char_id; + break; + case T_SHORT: + stub_id = StubId::stubgen_large_arrays_hashcode_short_id; + break; + case T_INT: + stub_id = StubId::stubgen_large_arrays_hashcode_int_id; + break; + default: + stub_id = StubId::NO_STUBID; + ShouldNotReachHere(); + }; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } const Register result = r0, ary = r1, cnt = r2; const FloatRegister vdata0 = v3, vdata1 = v2, vdata2 = v1, vdata3 = v0; const FloatRegister vmul0 = v4, vmul1 = v5, vmul2 = v6, vmul3 = v7; @@ -8472,28 +9170,6 @@ class StubGenerator: public StubCodeGenerator { __ align(CodeEntryAlignment); - StubId stub_id; - switch (eltype) { - case T_BOOLEAN: - stub_id = StubId::stubgen_large_arrays_hashcode_boolean_id; - break; - case T_BYTE: - stub_id = StubId::stubgen_large_arrays_hashcode_byte_id; - break; - case T_CHAR: - stub_id = StubId::stubgen_large_arrays_hashcode_char_id; - break; - case T_SHORT: - stub_id = StubId::stubgen_large_arrays_hashcode_short_id; - break; - case T_INT: - stub_id = StubId::stubgen_large_arrays_hashcode_int_id; - break; - default: - stub_id = StubId::NO_STUBID; - ShouldNotReachHere(); - }; - StubCodeMark mark(this, stub_id); address entry = __ pc(); @@ -8728,19 +9404,32 @@ class StubGenerator: public StubCodeGenerator { __ leave(); __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, entry, __ pc()); + return entry; } address generate_dsin_dcos(bool isCos) { - __ align(CodeEntryAlignment); StubId stub_id = (isCos ? StubId::stubgen_dcos_id : StubId::stubgen_dsin_id); + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ generate_dsin_dcos(isCos, (address)StubRoutines::aarch64::_npio2_hw, (address)StubRoutines::aarch64::_two_over_pi, (address)StubRoutines::aarch64::_pio2, (address)StubRoutines::aarch64::_dsin_coef, (address)StubRoutines::aarch64::_dcos_coef); + + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -8784,8 +9473,14 @@ class StubGenerator: public StubCodeGenerator { // r10 = tmp1 // r11 = tmp2 address generate_compare_long_string_different_encoding(bool isLU) { - __ align(CodeEntryAlignment); StubId stub_id = (isLU ? StubId::stubgen_compare_long_string_LU_id : StubId::stubgen_compare_long_string_UL_id); + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); address entry = __ pc(); Label SMALL_LOOP, TAIL, TAIL_LOAD_16, LOAD_LAST, DIFF1, DIFF2, @@ -8887,20 +9582,34 @@ class StubGenerator: public StubCodeGenerator { __ subw(result, tmp1, rscratch1); __ bind(DONE); __ ret(lr); - return entry; + + // record the stub entry and end + store_archive_data(stub_id, entry, __ pc()); + + return entry; } // r0 = input (float16) // v0 = result (float) // v1 = temporary float register address generate_float16ToFloat() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_hf2f_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); address entry = __ pc(); BLOCK_COMMENT("Entry:"); __ flt16_to_flt(v0, r0, v1); __ ret(lr); + + // record the stub entry and end + store_archive_data(stub_id, entry, __ pc()); + return entry; } @@ -8908,24 +9617,40 @@ class StubGenerator: public StubCodeGenerator { // r0 = result (float16) // v1 = temporary float register address generate_floatToFloat16() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_f2hf_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); address entry = __ pc(); BLOCK_COMMENT("Entry:"); __ flt_to_flt16(r0, v0, v1); __ ret(lr); + + // record the stub entry and end + store_archive_data(stub_id, entry, __ pc()); + return entry; } address generate_method_entry_barrier() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_method_entry_barrier_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); Label deoptimize_label; - address start = __ pc(); + start = __ pc(); BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler(); @@ -8974,6 +9699,9 @@ class StubGenerator: public StubCodeGenerator { __ mov(sp, rscratch1); __ br(rscratch2); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -8985,8 +9713,14 @@ class StubGenerator: public StubCodeGenerator { // r10 = tmp1 // r11 = tmp2 address generate_compare_long_string_same_encoding(bool isLL) { - __ align(CodeEntryAlignment); StubId stub_id = (isLL ? StubId::stubgen_compare_long_string_LL_id : StubId::stubgen_compare_long_string_UU_id); + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); address entry = __ pc(); Register result = r0, str1 = r1, cnt1 = r2, str2 = r3, cnt2 = r4, @@ -9094,6 +9828,10 @@ class StubGenerator: public StubCodeGenerator { __ bind(LENGTH_DIFF); __ ret(lr); + + // record the stub entry and end + store_archive_data(stub_id, entry, __ pc()); + return entry; } @@ -9125,8 +9863,14 @@ class StubGenerator: public StubCodeGenerator { case UU: stub_id = StubId::stubgen_compare_long_string_UU_id; break; default: ShouldNotReachHere(); } - + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); + StubCodeMark mark(this, stub_id); address entry = __ pc(); Register result = r0, str1 = r1, cnt1 = r2, str2 = r3, cnt2 = r4, tmp1 = r10, tmp2 = r11; @@ -9161,8 +9905,6 @@ class StubGenerator: public StubCodeGenerator { ShouldNotReachHere(); \ } - StubCodeMark mark(this, stub_id); - __ mov(idx, 0); __ sve_whilelt(pgtmp1, mode == LL ? __ B : __ H, idx, cnt); @@ -9206,6 +9948,10 @@ class StubGenerator: public StubCodeGenerator { __ bind(DONE); __ ret(lr); #undef LOAD_PAIR + + // record the stub entry and end + store_archive_data(stub_id, entry, __ pc()); + return entry; } @@ -9267,6 +10013,12 @@ class StubGenerator: public StubCodeGenerator { stub_id = StubId::stubgen_string_indexof_linear_uu_id; } } + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); address entry = __ pc(); @@ -9535,6 +10287,10 @@ class StubGenerator: public StubCodeGenerator { __ BIND(DONE); __ pop(spilled_regs, sp); __ ret(lr); + + // record the stub entry and end + store_archive_data(stub_id, entry, __ pc()); + return entry; } @@ -9565,8 +10321,14 @@ class StubGenerator: public StubCodeGenerator { // v1 = loaded 8 bytes // Clobbers: r0, r1, r3, rscratch1, rflags, v0-v6 address generate_large_byte_array_inflate() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_large_byte_array_inflate_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); address entry = __ pc(); Label LOOP, LOOP_START, LOOP_PRFM, LOOP_PRFM_START, DONE; @@ -9605,6 +10367,10 @@ class StubGenerator: public StubCodeGenerator { __ br(__ GE, LOOP); __ bind(DONE); __ ret(lr); + + // record the stub entry and end + store_archive_data(stub_id, entry, __ pc()); + return entry; } @@ -9620,7 +10386,7 @@ class StubGenerator: public StubCodeGenerator { * Output: * Updated state at c_rarg0 */ - address generate_ghash_processBlocks() { + address generate_ghash_processBlocks_small() { // Bafflingly, GCM uses little-endian for the byte order, but // big-endian for the bit order. For example, the polynomial 1 is // represented as the 16-byte string 80 00 00 00 | 12 bytes of 00. @@ -9632,11 +10398,17 @@ class StubGenerator: public StubCodeGenerator { // that) and keep the data in little-endian bit order through the // calculation, bit-reversing the inputs and outputs. - StubId stub_id = StubId::stubgen_ghash_processBlocks_id; + StubId stub_id = StubId::stubgen_ghash_processBlocks_small_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); Label polynomial; // local data generated at end of stub - __ align(CodeEntryAlignment); - address start = __ pc(); + start = __ pc(); Register state = c_rarg0; Register subkeyH = c_rarg1; @@ -9696,17 +10468,24 @@ class StubGenerator: public StubCodeGenerator { // 128-bit vector __ emit_int64(0x87); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } - address generate_ghash_processBlocks_wide() { - address small = generate_ghash_processBlocks(); - - StubId stub_id = StubId::stubgen_ghash_processBlocks_wide_id; - StubCodeMark mark(this, stub_id); + address generate_ghash_processBlocks(address small) { + StubId stub_id = StubId::stubgen_ghash_processBlocks_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } Label polynomial; // local data generated after stub __ align(CodeEntryAlignment); - address start = __ pc(); + StubCodeMark mark(this, stub_id); + start = __ pc(); Register state = c_rarg0; Register subkeyH = c_rarg1; @@ -9748,8 +10527,10 @@ class StubGenerator: public StubCodeGenerator { // 128-bit vector __ emit_int64(0x87); - return start; + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } void generate_base64_encode_simdround(Register src, Register dst, @@ -9800,26 +10581,16 @@ class StubGenerator: public StubCodeGenerator { */ address generate_base64_encodeBlock() { - static const char toBase64[64] = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', - 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', - 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' - }; - - static const char toBase64URL[64] = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', - 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', - 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_' - }; - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_base64_encodeBlock_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Register src = c_rarg0; // source array Register soff = c_rarg1; // source start offset @@ -9839,9 +10610,9 @@ class StubGenerator: public StubCodeGenerator { __ sub(length, send, soff); // load the codec base address - __ lea(codec, ExternalAddress((address) toBase64)); + __ lea(codec, ExternalAddress((address) _encodeBlock_toBase64)); __ cbz(isURL, ProcessData); - __ lea(codec, ExternalAddress((address) toBase64URL)); + __ lea(codec, ExternalAddress((address) _encodeBlock_toBase64URL)); __ BIND(ProcessData); @@ -9894,6 +10665,9 @@ class StubGenerator: public StubCodeGenerator { __ BIND(Exit); __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -10015,80 +10789,16 @@ class StubGenerator: public StubCodeGenerator { // on http://0x80.pl/articles/base64-simd-neon.html#encoding-quadwords, in section // titled "Base64 decoding". - // Non-SIMD lookup tables are mostly dumped from fromBase64 array used in java.util.Base64, - // except the trailing character '=' is also treated illegal value in this intrinsic. That - // is java.util.Base64.fromBase64['='] = -2, while fromBase(URL)64ForNoSIMD['='] = 255 here. - static const uint8_t fromBase64ForNoSIMD[256] = { - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 62u, 255u, 255u, 255u, 63u, - 52u, 53u, 54u, 55u, 56u, 57u, 58u, 59u, 60u, 61u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 11u, 12u, 13u, 14u, - 15u, 16u, 17u, 18u, 19u, 20u, 21u, 22u, 23u, 24u, 25u, 255u, 255u, 255u, 255u, 255u, - 255u, 26u, 27u, 28u, 29u, 30u, 31u, 32u, 33u, 34u, 35u, 36u, 37u, 38u, 39u, 40u, - 41u, 42u, 43u, 44u, 45u, 46u, 47u, 48u, 49u, 50u, 51u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - }; - - static const uint8_t fromBase64URLForNoSIMD[256] = { - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 62u, 255u, 255u, - 52u, 53u, 54u, 55u, 56u, 57u, 58u, 59u, 60u, 61u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 11u, 12u, 13u, 14u, - 15u, 16u, 17u, 18u, 19u, 20u, 21u, 22u, 23u, 24u, 25u, 255u, 255u, 255u, 255u, 63u, - 255u, 26u, 27u, 28u, 29u, 30u, 31u, 32u, 33u, 34u, 35u, 36u, 37u, 38u, 39u, 40u, - 41u, 42u, 43u, 44u, 45u, 46u, 47u, 48u, 49u, 50u, 51u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - }; - - // A legal value of base64 code is in range [0, 127]. We need two lookups - // with tbl/tbx and combine them to get the decode data. The 1st table vector - // lookup use tbl, out of range indices are set to 0 in destination. The 2nd - // table vector lookup use tbx, out of range indices are unchanged in - // destination. Input [64..126] is mapped to index [65, 127] in second lookup. - // The value of index 64 is set to 0, so that we know that we already get the - // decoded data with the 1st lookup. - static const uint8_t fromBase64ForSIMD[128] = { - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 62u, 255u, 255u, 255u, 63u, - 52u, 53u, 54u, 55u, 56u, 57u, 58u, 59u, 60u, 61u, 255u, 255u, 255u, 255u, 255u, 255u, - 0u, 255u, 0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 11u, 12u, 13u, - 14u, 15u, 16u, 17u, 18u, 19u, 20u, 21u, 22u, 23u, 24u, 25u, 255u, 255u, 255u, 255u, - 255u, 255u, 26u, 27u, 28u, 29u, 30u, 31u, 32u, 33u, 34u, 35u, 36u, 37u, 38u, 39u, - 40u, 41u, 42u, 43u, 44u, 45u, 46u, 47u, 48u, 49u, 50u, 51u, 255u, 255u, 255u, 255u, - }; - - static const uint8_t fromBase64URLForSIMD[128] = { - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 62u, 255u, 255u, - 52u, 53u, 54u, 55u, 56u, 57u, 58u, 59u, 60u, 61u, 255u, 255u, 255u, 255u, 255u, 255u, - 0u, 255u, 0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 11u, 12u, 13u, - 14u, 15u, 16u, 17u, 18u, 19u, 20u, 21u, 22u, 23u, 24u, 25u, 255u, 255u, 255u, 255u, - 63u, 255u, 26u, 27u, 28u, 29u, 30u, 31u, 32u, 33u, 34u, 35u, 36u, 37u, 38u, 39u, - 40u, 41u, 42u, 43u, 44u, 45u, 46u, 47u, 48u, 49u, 50u, 51u, 255u, 255u, 255u, 255u, - }; - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_base64_decodeBlock_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Register src = c_rarg0; // source array Register soff = c_rarg1; // source start offset @@ -10115,9 +10825,9 @@ class StubGenerator: public StubCodeGenerator { __ sub(length, send, soff); __ bfm(length, zr, 0, 1); - __ lea(nosimd_codec, ExternalAddress((address) fromBase64ForNoSIMD)); + __ lea(nosimd_codec, ExternalAddress((address) _decodeBlock_fromBase64ForNoSIMD)); __ cbz(isURL, ProcessData); - __ lea(nosimd_codec, ExternalAddress((address) fromBase64URLForNoSIMD)); + __ lea(nosimd_codec, ExternalAddress((address) _decodeBlock_fromBase64URLForNoSIMD)); __ BIND(ProcessData); __ mov(rscratch1, length); @@ -10162,9 +10872,9 @@ class StubGenerator: public StubCodeGenerator { __ cbzw(rscratch1, Exit); __ sub(length, length, 80); - __ lea(simd_codec, ExternalAddress((address) fromBase64ForSIMD)); + __ lea(simd_codec, ExternalAddress((address) _decodeBlock_fromBase64ForSIMD)); __ cbz(isURL, SIMDEnter); - __ lea(simd_codec, ExternalAddress((address) fromBase64URLForSIMD)); + __ lea(simd_codec, ExternalAddress((address) _decodeBlock_fromBase64URLForSIMD)); __ BIND(SIMDEnter); __ ld1(v0, v1, v2, v3, __ T16B, __ post(simd_codec, 64)); @@ -10197,24 +10907,50 @@ class StubGenerator: public StubCodeGenerator { __ leave(); __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } // Support for spin waits. address generate_spin_wait() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_spin_wait_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ spin_wait(); __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } void generate_lookup_secondary_supers_table_stub() { StubId stub_id = StubId::stubgen_lookup_secondary_supers_table_id; + GrowableArray
entries; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == Klass::SECONDARY_SUPERS_TABLE_SIZE, "sanity check"); + address start = load_archive_data(stub_id, &entries); + if (start != nullptr) { + assert(entries.length() == Klass::SECONDARY_SUPERS_TABLE_SIZE - 1, + "unexpected extra entry count %d", entries.length()); + StubRoutines::_lookup_secondary_supers_table_stubs[0] = start; + for (int slot = 1; slot < Klass::SECONDARY_SUPERS_TABLE_SIZE; slot++) { + StubRoutines::_lookup_secondary_supers_table_stubs[slot] = entries.at(slot - 1); + } + return; + } + StubCodeMark mark(this, stub_id); const Register @@ -10229,7 +10965,13 @@ class StubGenerator: public StubCodeGenerator { vtemp = v0; for (int slot = 0; slot < Klass::SECONDARY_SUPERS_TABLE_SIZE; slot++) { - StubRoutines::_lookup_secondary_supers_table_stubs[slot] = __ pc(); + address next_entry = __ pc(); + StubRoutines::_lookup_secondary_supers_table_stubs[slot] = next_entry; + if (slot == 0) { + start = next_entry; + } else { + entries.append(next_entry); + } Label L_success; __ enter(); __ lookup_secondary_supers_table_const(r_sub_klass, r_super_klass, @@ -10239,14 +10981,21 @@ class StubGenerator: public StubCodeGenerator { __ leave(); __ ret(lr); } + // record the stub entry and end plus all the auxiliary entries + store_archive_data(stub_id, start, __ pc(), &entries); } // Slow path implementation for UseSecondarySupersTable. address generate_lookup_secondary_supers_table_slow_path_stub() { StubId stub_id = StubId::stubgen_lookup_secondary_supers_table_slow_path_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - - address start = __ pc(); + start = __ pc(); const Register r_super_klass = r0, // argument r_array_base = r1, // argument @@ -10258,6 +11007,9 @@ class StubGenerator: public StubCodeGenerator { __ lookup_secondary_supers_table_slow_path(r_super_klass, r_array_base, r_array_index, r_bitmap, temp1, result); __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -10397,14 +11149,43 @@ class StubGenerator: public StubCodeGenerator { if (! UseLSE) { return; } - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_atomic_entry_points_id; - StubCodeMark mark(this, stub_id); - address first_entry = __ pc(); + GrowableArray
entries; + int entry_count = StubInfo::entry_count(stub_id); + address start = load_archive_data(stub_id, &entries); + if (start != nullptr) { + assert(entries.length() == entry_count - 1, + "unexpected extra entry count %d", entries.length()); + aarch64_atomic_fetch_add_4_impl = (aarch64_atomic_stub_t)start; + int idx = 0; + aarch64_atomic_fetch_add_8_impl = (aarch64_atomic_stub_t)entries.at(idx++); + aarch64_atomic_fetch_add_4_relaxed_impl = (aarch64_atomic_stub_t)entries.at(idx++); + aarch64_atomic_fetch_add_8_relaxed_impl = (aarch64_atomic_stub_t)entries.at(idx++); + aarch64_atomic_xchg_4_impl = (aarch64_atomic_stub_t)entries.at(idx++); + aarch64_atomic_xchg_8_impl = (aarch64_atomic_stub_t)entries.at(idx++); + aarch64_atomic_cmpxchg_1_impl = (aarch64_atomic_stub_t)entries.at(idx++); + aarch64_atomic_cmpxchg_4_impl = (aarch64_atomic_stub_t)entries.at(idx++); + aarch64_atomic_cmpxchg_8_impl = (aarch64_atomic_stub_t)entries.at(idx++); + aarch64_atomic_cmpxchg_1_relaxed_impl = (aarch64_atomic_stub_t)entries.at(idx++); + aarch64_atomic_cmpxchg_4_relaxed_impl = (aarch64_atomic_stub_t)entries.at(idx++); + aarch64_atomic_cmpxchg_8_relaxed_impl = (aarch64_atomic_stub_t)entries.at(idx++); + aarch64_atomic_cmpxchg_4_release_impl = (aarch64_atomic_stub_t)entries.at(idx++); + aarch64_atomic_cmpxchg_8_release_impl = (aarch64_atomic_stub_t)entries.at(idx++); + aarch64_atomic_cmpxchg_4_seq_cst_impl = (aarch64_atomic_stub_t)entries.at(idx++); + aarch64_atomic_cmpxchg_8_seq_cst_impl = (aarch64_atomic_stub_t)entries.at(idx++); + assert(idx == entries.length(), "sanity!"); + return; + } + __ align(CodeEntryAlignment); + StubCodeMark mark(this, stub_id); + start = __ pc(); + address end; + { // ADD, memory_order_conservative AtomicStubMark mark_fetch_add_4(_masm, &aarch64_atomic_fetch_add_4_impl); gen_ldadd_entry(Assembler::word, memory_order_conservative); + AtomicStubMark mark_fetch_add_8(_masm, &aarch64_atomic_fetch_add_8_impl); gen_ldadd_entry(Assembler::xword, memory_order_conservative); @@ -10412,6 +11193,7 @@ class StubGenerator: public StubCodeGenerator { AtomicStubMark mark_fetch_add_4_relaxed (_masm, &aarch64_atomic_fetch_add_4_relaxed_impl); gen_ldadd_entry(MacroAssembler::word, memory_order_relaxed); + AtomicStubMark mark_fetch_add_8_relaxed (_masm, &aarch64_atomic_fetch_add_8_relaxed_impl); gen_ldadd_entry(MacroAssembler::xword, memory_order_relaxed); @@ -10419,14 +11201,17 @@ class StubGenerator: public StubCodeGenerator { // XCHG, memory_order_conservative AtomicStubMark mark_xchg_4(_masm, &aarch64_atomic_xchg_4_impl); gen_swpal_entry(Assembler::word); - AtomicStubMark mark_xchg_8_impl(_masm, &aarch64_atomic_xchg_8_impl); + + AtomicStubMark mark_xchg_8(_masm, &aarch64_atomic_xchg_8_impl); gen_swpal_entry(Assembler::xword); // CAS, memory_order_conservative AtomicStubMark mark_cmpxchg_1(_masm, &aarch64_atomic_cmpxchg_1_impl); gen_cas_entry(MacroAssembler::byte, memory_order_conservative); + AtomicStubMark mark_cmpxchg_4(_masm, &aarch64_atomic_cmpxchg_4_impl); gen_cas_entry(MacroAssembler::word, memory_order_conservative); + AtomicStubMark mark_cmpxchg_8(_masm, &aarch64_atomic_cmpxchg_8_impl); gen_cas_entry(MacroAssembler::xword, memory_order_conservative); @@ -10434,9 +11219,11 @@ class StubGenerator: public StubCodeGenerator { AtomicStubMark mark_cmpxchg_1_relaxed (_masm, &aarch64_atomic_cmpxchg_1_relaxed_impl); gen_cas_entry(MacroAssembler::byte, memory_order_relaxed); + AtomicStubMark mark_cmpxchg_4_relaxed (_masm, &aarch64_atomic_cmpxchg_4_relaxed_impl); gen_cas_entry(MacroAssembler::word, memory_order_relaxed); + AtomicStubMark mark_cmpxchg_8_relaxed (_masm, &aarch64_atomic_cmpxchg_8_relaxed_impl); gen_cas_entry(MacroAssembler::xword, memory_order_relaxed); @@ -10444,6 +11231,7 @@ class StubGenerator: public StubCodeGenerator { AtomicStubMark mark_cmpxchg_4_release (_masm, &aarch64_atomic_cmpxchg_4_release_impl); gen_cas_entry(MacroAssembler::word, memory_order_release); + AtomicStubMark mark_cmpxchg_8_release (_masm, &aarch64_atomic_cmpxchg_8_release_impl); gen_cas_entry(MacroAssembler::xword, memory_order_release); @@ -10451,11 +11239,41 @@ class StubGenerator: public StubCodeGenerator { AtomicStubMark mark_cmpxchg_4_seq_cst (_masm, &aarch64_atomic_cmpxchg_4_seq_cst_impl); gen_cas_entry(MacroAssembler::word, memory_order_seq_cst); + AtomicStubMark mark_cmpxchg_8_seq_cst (_masm, &aarch64_atomic_cmpxchg_8_seq_cst_impl); gen_cas_entry(MacroAssembler::xword, memory_order_seq_cst); - ICache::invalidate_range(first_entry, __ pc() - first_entry); + end = __ pc(); + + ICache::invalidate_range(start, end - start); + // exit block to force update of AtomicStubMark targets + } + + assert(start == (address)aarch64_atomic_fetch_add_4_impl, + "atomic stub should be at start of buffer"); + // record the stub start and end plus all the entries saved by the + // AtomicStubMark destructor + entries.append((address)aarch64_atomic_fetch_add_8_impl); + entries.append((address)aarch64_atomic_fetch_add_4_relaxed_impl); + entries.append((address)aarch64_atomic_fetch_add_8_relaxed_impl); + entries.append((address)aarch64_atomic_xchg_4_impl); + entries.append((address)aarch64_atomic_xchg_8_impl); + entries.append((address)aarch64_atomic_cmpxchg_1_impl); + entries.append((address)aarch64_atomic_cmpxchg_4_impl); + entries.append((address)aarch64_atomic_cmpxchg_8_impl); + entries.append((address)aarch64_atomic_cmpxchg_1_relaxed_impl); + entries.append((address)aarch64_atomic_cmpxchg_4_relaxed_impl); + entries.append((address)aarch64_atomic_cmpxchg_8_relaxed_impl); + entries.append((address)aarch64_atomic_cmpxchg_4_release_impl); + entries.append((address)aarch64_atomic_cmpxchg_8_release_impl); + entries.append((address)aarch64_atomic_cmpxchg_4_seq_cst_impl); + entries.append((address)aarch64_atomic_cmpxchg_8_seq_cst_impl); + + assert(entries.length() == entry_count - 1, + "unexpected extra entry count %d", entries.length()); + + store_archive_data(stub_id, start, end, &entries); } #endif // LINUX @@ -10559,9 +11377,19 @@ class StubGenerator: public StubCodeGenerator { if (!Continuations::enabled()) return nullptr; StubId stub_id = StubId::stubgen_cont_thaw_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); generate_cont_thaw(Continuation::thaw_top); + + // record the stub start and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -10570,11 +11398,20 @@ class StubGenerator: public StubCodeGenerator { // TODO: will probably need multiple return barriers depending on return type StubId stub_id = StubId::stubgen_cont_returnBarrier_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); generate_cont_thaw(Continuation::thaw_return_barrier); + // record the stub start and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -10582,19 +11419,34 @@ class StubGenerator: public StubCodeGenerator { if (!Continuations::enabled()) return nullptr; StubId stub_id = StubId::stubgen_cont_returnBarrierExc_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); generate_cont_thaw(Continuation::thaw_return_barrier_exception); + // record the stub start and end + store_archive_data(stub_id, start, __ pc()); + return start; } address generate_cont_preempt_stub() { if (!Continuations::enabled()) return nullptr; StubId stub_id = StubId::stubgen_cont_preempt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ reset_last_Java_frame(true); @@ -10619,6 +11471,9 @@ class StubGenerator: public StubCodeGenerator { __ ldr(rscratch1, Address(rscratch1)); __ br(rscratch1); + // record the stub start and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -10674,10 +11529,16 @@ class StubGenerator: public StubCodeGenerator { // computation. address generate_poly1305_processBlocks() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_poly1305_processBlocks_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label here; __ enter(); RegSet callee_saved = RegSet::range(r19, r28); @@ -10785,14 +11646,23 @@ class StubGenerator: public StubCodeGenerator { __ leave(); __ ret(lr); + // record the stub start and end + store_archive_data(stub_id, start, __ pc()); + return start; } // exception handler for upcall stubs address generate_upcall_stub_exception_handler() { StubId stub_id = StubId::stubgen_upcall_stub_exception_handler_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); // Native caller has no idea how to handle exceptions, // so we just crash here. Up to callee to catch exceptions. @@ -10801,6 +11671,9 @@ class StubGenerator: public StubCodeGenerator { __ blr(rscratch1); __ should_not_reach_here(); + // record the stub start and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -10809,8 +11682,14 @@ class StubGenerator: public StubCodeGenerator { // rmethod = result address generate_upcall_stub_load_target() { StubId stub_id = StubId::stubgen_upcall_stub_load_target_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ resolve_global_jobject(j_rarg0, rscratch1, rscratch2); // Load target method from receiver @@ -10824,6 +11703,9 @@ class StubGenerator: public StubCodeGenerator { __ ret(lr); + // record the stub start and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -11223,8 +12105,6 @@ class StubGenerator: public StubCodeGenerator { */ address generate_multiply() { Label argh, nothing; - bind(argh); - stop("MontgomeryMultiply total_allocation must be <= 8192"); align(CodeEntryAlignment); address entry = pc(); @@ -11331,6 +12211,10 @@ class StubGenerator: public StubCodeGenerator { bind(nothing); ret(lr); + // handler for error case + bind(argh); + stop("MontgomeryMultiply total_allocation must be <= 8192"); + return entry; } // In C, approximately: @@ -11434,8 +12318,6 @@ class StubGenerator: public StubCodeGenerator { */ address generate_square() { Label argh; - bind(argh); - stop("MontgomeryMultiply total_allocation must be <= 8192"); align(CodeEntryAlignment); address entry = pc(); @@ -11544,6 +12426,10 @@ class StubGenerator: public StubCodeGenerator { leave(); ret(lr); + // handler for error case + bind(argh); + stop("MontgomeryMultiply total_allocation must be <= 8192"); + return entry; } // In C, approximately: @@ -11753,7 +12639,7 @@ class StubGenerator: public StubCodeGenerator { #if COMPILER2_OR_JVMCI if (UseSVE == 0) { - StubRoutines::aarch64::_vector_iota_indices = generate_iota_indices(StubId::stubgen_vector_iota_indices_id); + generate_iota_indices(StubId::stubgen_vector_iota_indices_id); } // array equals stub for large arrays. @@ -11798,18 +12684,32 @@ class StubGenerator: public StubCodeGenerator { if (UseMontgomeryMultiplyIntrinsic) { StubId stub_id = StubId::stubgen_montgomeryMultiply_id; - StubCodeMark mark(this, stub_id); - MontgomeryMultiplyGenerator g(_masm, /*squaring*/false); - StubRoutines::_montgomeryMultiply = g.generate_multiply(); + address start = load_archive_data(stub_id); + if (start == nullptr) { + // we have to generate it + StubCodeMark mark(this, stub_id); + MontgomeryMultiplyGenerator g(_masm, /*squaring*/false); + start = g.generate_multiply(); + // record the stub start and end + store_archive_data(stub_id, start, _masm->pc()); + } + StubRoutines::_montgomeryMultiply = start; } if (UseMontgomerySquareIntrinsic) { StubId stub_id = StubId::stubgen_montgomerySquare_id; - StubCodeMark mark(this, stub_id); - MontgomeryMultiplyGenerator g(_masm, /*squaring*/true); - // We use generate_multiply() rather than generate_square() - // because it's faster for the sizes of modulus we care about. - StubRoutines::_montgomerySquare = g.generate_multiply(); + address start = load_archive_data(stub_id); + if (start == nullptr) { + // we have to generate it + StubCodeMark mark(this, stub_id); + MontgomeryMultiplyGenerator g(_masm, /*squaring*/true); + // We use generate_multiply() rather than generate_square() + // because it's faster for the sizes of modulus we care about. + start = g.generate_multiply(); + // record the stub start and end + store_archive_data(stub_id, start, _masm->pc()); + } + StubRoutines::_montgomerySquare = start; } #endif // COMPILER2 @@ -11854,7 +12754,8 @@ class StubGenerator: public StubCodeGenerator { } if (UseGHASHIntrinsics) { // StubRoutines::_ghash_processBlocks = generate_ghash_processBlocks(); - StubRoutines::_ghash_processBlocks = generate_ghash_processBlocks_wide(); + StubRoutines::aarch64::_ghash_processBlocks_small = generate_ghash_processBlocks_small(); + StubRoutines::_ghash_processBlocks = generate_ghash_processBlocks(StubRoutines::aarch64::_ghash_processBlocks_small); } if (UseAESIntrinsics && UseGHASHIntrinsics) { StubRoutines::_galoisCounterMode_AESCrypt = generate_galoisCounterMode_AESCrypt(); @@ -11898,7 +12799,7 @@ class StubGenerator: public StubCodeGenerator { } public: - StubGenerator(CodeBuffer* code, BlobId blob_id) : StubCodeGenerator(code, blob_id) { + StubGenerator(CodeBuffer* code, BlobId blob_id, AOTStubData* stub_data) : StubCodeGenerator(code, blob_id, stub_data) { switch(blob_id) { case BlobId::stubgen_preuniverse_id: generate_preuniverse_stubs(); @@ -11920,12 +12821,35 @@ class StubGenerator: public StubCodeGenerator { break; }; } + +#if INCLUDE_CDS + static void init_AOTAddressTable(GrowableArray
& external_addresses) { + // external data defined in this file +#define ADD(addr) external_addresses.append((address)(addr)); + ADD(_sha256_round_consts); + ADD(_sha512_round_consts); + ADD(_sha3_round_consts); + ADD(_double_keccak_round_consts); + ADD(_encodeBlock_toBase64); + ADD(_encodeBlock_toBase64URL); + ADD(_decodeBlock_fromBase64ForNoSIMD); + ADD(_decodeBlock_fromBase64URLForNoSIMD); + ADD(_decodeBlock_fromBase64ForSIMD); + ADD(_decodeBlock_fromBase64URLForSIMD); +#undef ADD + } +#endif // INCLUDE_CDS }; // end class declaration -void StubGenerator_generate(CodeBuffer* code, BlobId blob_id) { - StubGenerator g(code, blob_id); +void StubGenerator_generate(CodeBuffer* code, BlobId blob_id, AOTStubData* stub_data) { + StubGenerator g(code, blob_id, stub_data); } +#if INCLUDE_CDS +void StubGenerator_init_AOTAddressTable(GrowableArray
& addresses) { + StubGenerator::init_AOTAddressTable(addresses); +} +#endif // INCLUDE_CDS #if defined (LINUX) diff --git a/src/hotspot/cpu/aarch64/stubRoutines_aarch64.cpp b/src/hotspot/cpu/aarch64/stubRoutines_aarch64.cpp index 88993818b47..f02b681ca10 100644 --- a/src/hotspot/cpu/aarch64/stubRoutines_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/stubRoutines_aarch64.cpp @@ -41,8 +41,12 @@ static void empty_spin_wait() { } #define DEFINE_ARCH_ENTRY_INIT(arch, blob_name, stub_name, field_name, getter_name, init_function) \ address StubRoutines:: arch :: STUB_FIELD_NAME(field_name) = CAST_FROM_FN_PTR(address, init_function); -STUBGEN_ARCH_ENTRIES_DO(DEFINE_ARCH_ENTRY, DEFINE_ARCH_ENTRY_INIT) +#define DEFINE_ARCH_ENTRY_ARRAY(arch, blob_name, stub_name, field_name, getter_name, count) \ + address StubRoutines:: arch :: STUB_FIELD_NAME(field_name) [count]; +STUBGEN_ARCH_ENTRIES_DO(DEFINE_ARCH_ENTRY, DEFINE_ARCH_ENTRY_INIT, DEFINE_ARCH_ENTRY_ARRAY) + +#undef DEFINE_ARCH_ENTRY_ARARAY #undef DEFINE_ARCH_ENTRY_INIT #undef DEFINE_ARCH_ENTRY @@ -413,3 +417,36 @@ ATTRIBUTE_ALIGNED(64) jdouble StubRoutines::aarch64::_pio2[] = { 2.73370053816464559624e-44, // 0x36E3822280000000 2.16741683877804819444e-51, // 0x3569F31D00000000 }; + +#if INCLUDE_CDS +extern void StubGenerator_init_AOTAddressTable(GrowableArray
& addresses); + +void StubRoutines::init_AOTAddressTable() { + ResourceMark rm; + GrowableArray
external_addresses; + // publish static addresses referred to by aarch64 generator + // n.b. we have to use use an extern call here because class + // StubGenerator, which provides the static method that knows how to + // add the relevant addresses, is declared in a source file rather + // than in a separately includeable header. + StubGenerator_init_AOTAddressTable(external_addresses); + // publish external data addresses defined in nested aarch64 class + StubRoutines::aarch64::init_AOTAddressTable(external_addresses); + AOTCodeCache::publish_external_addresses(external_addresses); +} + +void StubRoutines::aarch64::init_AOTAddressTable(GrowableArray
& external_addresses) { +#define ADD(addr) external_addresses.append((address)(addr)); + ADD(_kyberConsts); + ADD(_dilithiumConsts); + // this is added in generic code + // ADD(_crc_table); + ADD(_adler_table); + ADD(_npio2_hw); + ADD(_dsin_coef); + ADD(_dcos_coef); + ADD(_two_over_pi); + ADD(_pio2); +#undef ADD +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/aarch64/stubRoutines_aarch64.hpp b/src/hotspot/cpu/aarch64/stubRoutines_aarch64.hpp index c35371e1083..6067408ef13 100644 --- a/src/hotspot/cpu/aarch64/stubRoutines_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/stubRoutines_aarch64.hpp @@ -60,9 +60,13 @@ class aarch64 { #define DECLARE_ARCH_ENTRY_INIT(arch, blob_name, stub_name, field_name, getter_name, init_function) \ DECLARE_ARCH_ENTRY(arch, blob_name, stub_name, field_name, getter_name) -private: - STUBGEN_ARCH_ENTRIES_DO(DECLARE_ARCH_ENTRY, DECLARE_ARCH_ENTRY_INIT) +#define DECLARE_ARCH_ENTRY_ARRAY(arch, blob_name, stub_name, field_name, getter_name, count) \ + static address STUB_FIELD_NAME(field_name) [count]; +private: + STUBGEN_ARCH_ENTRIES_DO(DECLARE_ARCH_ENTRY, DECLARE_ARCH_ENTRY_INIT, DECLARE_ARCH_ENTRY_ARRAY) + +#undef DECLARE_ARCH_ENTRY_ARRAY #undef DECLARE_ARCH_ENTRY_INIT #undef DECLARE_ARCH_ENTRY @@ -78,8 +82,15 @@ private: #define DEFINE_ARCH_ENTRY_GETTER_INIT(arch, blob_name, stub_name, field_name, getter_name, init_function) \ DEFINE_ARCH_ENTRY_GETTER(arch, blob_name, stub_name, field_name, getter_name) - STUBGEN_ARCH_ENTRIES_DO(DEFINE_ARCH_ENTRY_GETTER, DEFINE_ARCH_ENTRY_GETTER_INIT) +#define DEFINE_ARCH_ENTRY_GETTER_ARRAY(arch, blob_name, stub_name, field_name, getter_name, count) \ + static address getter_name(int idx) { \ + assert(0 <= idx && idx < count, "entry array index out of range"); \ + return STUB_FIELD_NAME(field_name) [idx]; \ + } + STUBGEN_ARCH_ENTRIES_DO(DEFINE_ARCH_ENTRY_GETTER, DEFINE_ARCH_ENTRY_GETTER_INIT, DEFINE_ARCH_ENTRY_GETTER_ARRAY) + +#undef DEFINE_ARCH_ENTRY_GETTER_ARRAY #undef DEFINE_ARCH_ENTRY_GETTER_INIT #undef DEFINE_ARCH_ENTRY_GETTER @@ -110,6 +121,11 @@ private: _completed = true; } +#if INCLUDE_CDS + static void init_AOTAddressTable(GrowableArray
& external_addresses); +#endif // INCLUDE_CDS + + private: static uint16_t _kyberConsts[]; static uint32_t _dilithiumConsts[]; diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp index 4423d9c5b58..441bd4859fe 100644 --- a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp @@ -1,7 +1,7 @@ /* * Copyright (c) 1997, 2026, 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. + * Copyright 2025, 2026 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 @@ -24,6 +24,7 @@ * */ +#include "logging/log.hpp" #include "pauth_aarch64.hpp" #include "register_aarch64.hpp" #include "runtime/arguments.hpp" @@ -52,17 +53,56 @@ uintptr_t VM_Version::_pac_mask; SpinWait VM_Version::_spin_wait; +bool VM_Version::_cache_dic_enabled; +bool VM_Version::_cache_idc_enabled; +bool VM_Version::_ic_ivau_trapped; + const char* VM_Version::_features_names[MAX_CPU_FEATURES] = { nullptr }; static SpinWait get_spin_wait_desc() { - SpinWait spin_wait(OnSpinWaitInst, OnSpinWaitInstCount); + SpinWait spin_wait(OnSpinWaitInst, OnSpinWaitInstCount, OnSpinWaitDelay); if (spin_wait.inst() == SpinWait::SB && !VM_Version::supports_sb()) { vm_exit_during_initialization("OnSpinWaitInst is SB but current CPU does not support SB instruction"); } + if (spin_wait.inst() == SpinWait::WFET) { + if (!VM_Version::supports_wfxt()) { + vm_exit_during_initialization("OnSpinWaitInst is WFET but the CPU does not support the WFET instruction"); + } + + if (!VM_Version::supports_ecv()) { + vm_exit_during_initialization("The CPU does not support the FEAT_ECV required by the -XX:OnSpinWaitInst=wfet implementation"); + } + + if (!VM_Version::supports_sb()) { + vm_exit_during_initialization("The CPU does not support the SB instruction required by the -XX:OnSpinWaitInst=wfet implementation"); + } + + if (OnSpinWaitInstCount != 1) { + vm_exit_during_initialization("OnSpinWaitInstCount for OnSpinWaitInst 'wfet' must be 1"); + } + } else { + if (!FLAG_IS_DEFAULT(OnSpinWaitDelay)) { + vm_exit_during_initialization("OnSpinWaitDelay can only be used with -XX:OnSpinWaitInst=wfet"); + } + } + return spin_wait; } +static bool has_neoverse_n1_errata_1542419() { + const int major_rev_num = VM_Version::cpu_variant(); + const int minor_rev_num = VM_Version::cpu_revision(); + // Neoverse N1: 0xd0c + // Erratum 1542419 affects r3p0, r3p1 and r4p0. + // It is fixed in r4p1 and later revisions, which are not affected. + return (VM_Version::cpu_family() == VM_Version::CPU_ARM && + VM_Version::model_is(0xd0c) && + ((major_rev_num == 3 && minor_rev_num == 0) || + (major_rev_num == 3 && minor_rev_num == 1) || + (major_rev_num == 4 && minor_rev_num == 0))); +} + void VM_Version::initialize() { #define SET_CPU_FEATURE_NAME(id, name, bit) \ _features_names[bit] = XSTR(name); @@ -74,9 +114,14 @@ void VM_Version::initialize() { _supports_atomic_getset8 = true; _supports_atomic_getadd8 = true; - get_os_cpu_info(); + _cache_dic_enabled = false; + _cache_idc_enabled = false; + _ic_ivau_trapped = false; - int dcache_line = VM_Version::dcache_line_size(); + get_os_cpu_info(); + _cpu_features = _features; + + int dcache_line = dcache_line_size(); // Limit AllocatePrefetchDistance so that it does not exceed the // static constraint of 512 defined in runtime/globals.hpp. @@ -124,7 +169,7 @@ void VM_Version::initialize() { // if dcpop is available publish data cache line flush size via // generic field, otherwise let if default to zero thereby // disabling writeback - if (VM_Version::supports_dcpop()) { + if (supports_dcpop()) { _data_cache_line_flush_size = dcache_line; } } @@ -245,14 +290,24 @@ void VM_Version::initialize() { } } - if (FLAG_IS_DEFAULT(UseCRC32)) { - UseCRC32 = VM_Version::supports_crc32(); + if (supports_sha1() || supports_sha256() || + supports_sha3() || supports_sha512()) { + if (FLAG_IS_DEFAULT(UseSHA)) { + FLAG_SET_DEFAULT(UseSHA, true); + } else if (!UseSHA) { + clear_feature(CPU_SHA1); + clear_feature(CPU_SHA2); + clear_feature(CPU_SHA3); + clear_feature(CPU_SHA512); + } + } else if (UseSHA) { + warning("SHA instructions are not available on this CPU"); + FLAG_SET_DEFAULT(UseSHA, false); } - if (UseCRC32 && !VM_Version::supports_crc32()) { - warning("UseCRC32 specified, but not supported on this CPU"); - FLAG_SET_DEFAULT(UseCRC32, false); - } + CHECK_CPU_FEATURE(supports_crc32, CRC32); + CHECK_CPU_FEATURE(supports_lse, LSE); + CHECK_CPU_FEATURE(supports_aes, AES); if (_cpu == CPU_ARM && model_is_in({ CPU_MODEL_ARM_NEOVERSE_V1, CPU_MODEL_ARM_NEOVERSE_V2, @@ -265,7 +320,7 @@ void VM_Version::initialize() { } } - if (UseCryptoPmullForCRC32 && (!VM_Version::supports_pmull() || !VM_Version::supports_sha3() || !VM_Version::supports_crc32())) { + if (UseCryptoPmullForCRC32 && (!supports_pmull() || !supports_sha3() || !supports_crc32())) { warning("UseCryptoPmullForCRC32 specified, but not supported on this CPU"); FLAG_SET_DEFAULT(UseCryptoPmullForCRC32, false); } @@ -279,48 +334,40 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UseVectorizedMismatchIntrinsic, false); } - if (VM_Version::supports_lse()) { - if (FLAG_IS_DEFAULT(UseLSE)) - FLAG_SET_DEFAULT(UseLSE, true); - } else { - if (UseLSE) { - warning("UseLSE specified, but not supported on this CPU"); - FLAG_SET_DEFAULT(UseLSE, false); - } - } - - if (VM_Version::supports_aes()) { - UseAES = UseAES || FLAG_IS_DEFAULT(UseAES); - UseAESIntrinsics = - UseAESIntrinsics || (UseAES && FLAG_IS_DEFAULT(UseAESIntrinsics)); - if (UseAESIntrinsics && !UseAES) { - warning("UseAESIntrinsics enabled, but UseAES not, enabling"); - UseAES = true; + if (supports_aes()) { + if (FLAG_IS_DEFAULT(UseAESIntrinsics)) { + FLAG_SET_DEFAULT(UseAESIntrinsics, true); } if (FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) { FLAG_SET_DEFAULT(UseAESCTRIntrinsics, true); } } else { - if (UseAES) { - warning("AES instructions are not available on this CPU"); - FLAG_SET_DEFAULT(UseAES, false); - } - if (UseAESIntrinsics) { - warning("AES intrinsics are not available on this CPU"); - FLAG_SET_DEFAULT(UseAESIntrinsics, false); - } - if (UseAESCTRIntrinsics) { - warning("AES/CTR intrinsics are not available on this CPU"); - FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); + if (!UseAES) { + if (UseAESIntrinsics) { + warning("AES intrinsics require UseAES flag to be enabled. Intrinsics will be disabled."); + FLAG_SET_DEFAULT(UseAESIntrinsics, false); + } + if (UseAESCTRIntrinsics) { + warning("AES/CTR intrinsics require UseAES flag to be enabled. Intrinsics will be disabled."); + FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); + } + } else if (!cpu_supports_aes()) { + if (UseAESIntrinsics) { + warning("AES intrinsics are not available on this CPU"); + FLAG_SET_DEFAULT(UseAESIntrinsics, false); + } + if (UseAESCTRIntrinsics) { + warning("AES/CTR intrinsics are not available on this CPU"); + FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); + } } } - if (FLAG_IS_DEFAULT(UseCRC32Intrinsics)) { UseCRC32Intrinsics = true; } - if (VM_Version::supports_crc32()) { + if (supports_crc32()) { if (FLAG_IS_DEFAULT(UseCRC32CIntrinsics)) { FLAG_SET_DEFAULT(UseCRC32CIntrinsics, true); } @@ -337,17 +384,7 @@ void VM_Version::initialize() { UseMD5Intrinsics = true; } - if (VM_Version::supports_sha1() || VM_Version::supports_sha256() || - VM_Version::supports_sha3() || VM_Version::supports_sha512()) { - if (FLAG_IS_DEFAULT(UseSHA)) { - FLAG_SET_DEFAULT(UseSHA, true); - } - } else if (UseSHA) { - warning("SHA instructions are not available on this CPU"); - FLAG_SET_DEFAULT(UseSHA, false); - } - - if (UseSHA && VM_Version::supports_sha1()) { + if (UseSHA && supports_sha1()) { if (FLAG_IS_DEFAULT(UseSHA1Intrinsics)) { FLAG_SET_DEFAULT(UseSHA1Intrinsics, true); } @@ -356,7 +393,7 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UseSHA1Intrinsics, false); } - if (UseSHA && VM_Version::supports_sha256()) { + if (UseSHA && supports_sha256()) { if (FLAG_IS_DEFAULT(UseSHA256Intrinsics)) { FLAG_SET_DEFAULT(UseSHA256Intrinsics, true); } @@ -366,7 +403,7 @@ void VM_Version::initialize() { } if (UseSHA) { - // No need to check VM_Version::supports_sha3(), since a fallback GPR intrinsic implementation is provided. + // No need to check supports_sha3(), since a fallback GPR intrinsic implementation is provided. if (FLAG_IS_DEFAULT(UseSHA3Intrinsics)) { FLAG_SET_DEFAULT(UseSHA3Intrinsics, true); } @@ -376,7 +413,7 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UseSHA3Intrinsics, false); } - if (UseSHA3Intrinsics && VM_Version::supports_sha3()) { + if (UseSHA3Intrinsics && supports_sha3()) { // Auto-enable UseSIMDForSHA3Intrinsic on hardware with performance benefit. // Note that the evaluation of SHA3 extension Intrinsics shows better performance // on Apple and Qualcomm silicon but worse performance on Neoverse V1 and N2. @@ -386,12 +423,12 @@ void VM_Version::initialize() { } } } - if (UseSHA3Intrinsics && UseSIMDForSHA3Intrinsic && !VM_Version::supports_sha3()) { + if (UseSHA3Intrinsics && UseSIMDForSHA3Intrinsic && !supports_sha3()) { warning("Intrinsics for SHA3-224, SHA3-256, SHA3-384 and SHA3-512 crypto hash functions not available on this CPU."); FLAG_SET_DEFAULT(UseSHA3Intrinsics, false); } - if (UseSHA && VM_Version::supports_sha512()) { + if (UseSHA && supports_sha512()) { if (FLAG_IS_DEFAULT(UseSHA512Intrinsics)) { FLAG_SET_DEFAULT(UseSHA512Intrinsics, true); } @@ -400,11 +437,7 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UseSHA512Intrinsics, false); } - if (!(UseSHA1Intrinsics || UseSHA256Intrinsics || UseSHA3Intrinsics || UseSHA512Intrinsics)) { - FLAG_SET_DEFAULT(UseSHA, false); - } - - if (VM_Version::supports_pmull()) { + if (supports_pmull()) { if (FLAG_IS_DEFAULT(UseGHASHIntrinsics)) { FLAG_SET_DEFAULT(UseGHASHIntrinsics, true); } @@ -455,7 +488,7 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UseBlockZeroing, true); } if (FLAG_IS_DEFAULT(BlockZeroingLowLimit)) { - FLAG_SET_DEFAULT(BlockZeroingLowLimit, 4 * VM_Version::zva_length()); + FLAG_SET_DEFAULT(BlockZeroingLowLimit, 4 * zva_length()); } } else if (UseBlockZeroing) { if (!FLAG_IS_DEFAULT(UseBlockZeroing)) { @@ -464,11 +497,11 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UseBlockZeroing, false); } - if (VM_Version::supports_sve2()) { + if (supports_sve2()) { if (FLAG_IS_DEFAULT(UseSVE)) { FLAG_SET_DEFAULT(UseSVE, 2); } - } else if (VM_Version::supports_sve()) { + } else if (supports_sve()) { if (FLAG_IS_DEFAULT(UseSVE)) { FLAG_SET_DEFAULT(UseSVE, 1); } else if (UseSVE > 1) { @@ -519,7 +552,7 @@ void VM_Version::initialize() { // 1) this code has been built with branch-protection and // 2) the CPU/OS supports it #ifdef __ARM_FEATURE_PAC_DEFAULT - if (!VM_Version::supports_paca()) { + if (!supports_paca()) { // Disable PAC to prevent illegal instruction crashes. warning("ROP-protection specified, but not supported on this CPU. Disabling ROP-protection."); } else { @@ -661,6 +694,43 @@ void VM_Version::initialize() { clear_feature(CPU_SVE); } + if (FLAG_IS_DEFAULT(UseSingleICacheInvalidation) && is_cache_idc_enabled() && is_cache_dic_enabled()) { + FLAG_SET_DEFAULT(UseSingleICacheInvalidation, true); + } + + if (FLAG_IS_DEFAULT(NeoverseN1ICacheErratumMitigation) && has_neoverse_n1_errata_1542419() + && is_cache_idc_enabled() && !is_cache_dic_enabled()) { + if (_ic_ivau_trapped) { + FLAG_SET_DEFAULT(NeoverseN1ICacheErratumMitigation, true); + } else { + log_info(os)("IC IVAU is not trapped; disabling NeoverseN1ICacheErratumMitigation"); + FLAG_SET_DEFAULT(NeoverseN1ICacheErratumMitigation, false); + } + } + + if (NeoverseN1ICacheErratumMitigation) { + if (!has_neoverse_n1_errata_1542419()) { + vm_exit_during_initialization("NeoverseN1ICacheErratumMitigation is set for the CPU not having Neoverse N1 errata 1542419"); + } + // If the user explicitly set the flag, verify the trap is active. + if (!FLAG_IS_DEFAULT(NeoverseN1ICacheErratumMitigation) && !_ic_ivau_trapped) { + vm_exit_during_initialization("NeoverseN1ICacheErratumMitigation is set but IC IVAU is not trapped. " + "The optimization is not safe on this system."); + } + if (FLAG_IS_DEFAULT(UseSingleICacheInvalidation)) { + FLAG_SET_DEFAULT(UseSingleICacheInvalidation, true); + } + + if (!UseSingleICacheInvalidation) { + vm_exit_during_initialization("NeoverseN1ICacheErratumMitigation is set but UseSingleICacheInvalidation is not enabled"); + } + } + + if (UseSingleICacheInvalidation + && (!is_cache_idc_enabled() || (!is_cache_dic_enabled() && !NeoverseN1ICacheErratumMitigation))) { + vm_exit_during_initialization("UseSingleICacheInvalidation is set but neither IDC nor DIC nor NeoverseN1ICacheErratumMitigation is enabled"); + } + // Construct the "features" string stringStream ss(512); ss.print("0x%02x:0x%x:0x%03x:%d", _cpu, _variant, _model, _revision); diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp index e8681611234..30f1a5d86ca 100644 --- a/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp @@ -58,6 +58,12 @@ protected: // When _prefer_sve_merging_mode_cpy is true, `cpy (imm, zeroing)` is // implemented as `movi; cpy(imm, merging)`. static constexpr bool _prefer_sve_merging_mode_cpy = true; + static bool _cache_dic_enabled; + static bool _cache_idc_enabled; + + // IC IVAU trap probe for Neoverse N1 erratum 1542419. + // Set by get_os_cpu_info() on Linux via ic_ivau_probe_linux_aarch64.S. + static bool _ic_ivau_trapped; static SpinWait _spin_wait; @@ -159,7 +165,9 @@ public: /* flags above must follow Linux HWCAP */ \ decl(SVEBITPERM, svebitperm, 27) \ decl(SVE2, sve2, 28) \ - decl(A53MAC, a53mac, 31) + decl(A53MAC, a53mac, 31) \ + decl(ECV, ecv, 32) \ + decl(WFXT, wfxt, 33) enum Feature_Flag { #define DECLARE_CPU_FEATURE_FLAG(id, name, bit) CPU_##id = bit, @@ -191,6 +199,8 @@ public: return (features & BIT_MASK(flag)) != 0; } + static bool cpu_supports_aes() { return supports_feature(_cpu_features, CPU_AES); } + static int cpu_family() { return _cpu; } static int cpu_model() { return _model; } static int cpu_model2() { return _model2; } @@ -253,6 +263,10 @@ public: return vector_length_in_bytes <= 16; } + static bool is_cache_dic_enabled() { return _cache_dic_enabled; } + static bool is_cache_idc_enabled() { return _cache_idc_enabled; } + static bool is_ic_ivau_trapped() { return _ic_ivau_trapped; } + static void get_cpu_features_name(void* features_buffer, stringStream& ss); // Returns names of features present in features_set1 but not in features_set2 diff --git a/src/hotspot/cpu/arm/c1_LIRGenerator_arm.cpp b/src/hotspot/cpu/arm/c1_LIRGenerator_arm.cpp index 4c339968f85..46ec87290ae 100644 --- a/src/hotspot/cpu/arm/c1_LIRGenerator_arm.cpp +++ b/src/hotspot/cpu/arm/c1_LIRGenerator_arm.cpp @@ -1332,7 +1332,8 @@ void LIRGenerator::volatile_field_load(LIR_Address* address, LIR_Opr result, load_addr = address; } __ volatile_load_mem_reg(load_addr, result, info); - return; + } else { + __ load(address, result, info, lir_patch_none); } - __ load(address, result, info, lir_patch_none); + __ membar_acquire(); } diff --git a/src/hotspot/cpu/arm/interp_masm_arm.cpp b/src/hotspot/cpu/arm/interp_masm_arm.cpp index 23ecea24eb2..aee407864ee 100644 --- a/src/hotspot/cpu/arm/interp_masm_arm.cpp +++ b/src/hotspot/cpu/arm/interp_masm_arm.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2026, 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 @@ -1210,7 +1210,7 @@ void InterpreterMacroAssembler::profile_final_call(Register mdp) { // Sets mdp, blows Rtemp. -void InterpreterMacroAssembler::profile_virtual_call(Register mdp, Register receiver, bool receiver_can_be_null) { +void InterpreterMacroAssembler::profile_virtual_call(Register mdp, Register receiver) { assert_different_registers(mdp, receiver, Rtemp); if (ProfileInterpreter) { @@ -1219,19 +1219,8 @@ void InterpreterMacroAssembler::profile_virtual_call(Register mdp, Register rece // If no method data exists, go to profile_continue. test_method_data_pointer(mdp, profile_continue); - Label skip_receiver_profile; - if (receiver_can_be_null) { - Label not_null; - cbnz(receiver, not_null); - // We are making a call. Increment the count for null receiver. - increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset()), Rtemp); - b(skip_receiver_profile); - bind(not_null); - } - // Record the receiver type. record_klass_in_profile(receiver, mdp, Rtemp, true); - bind(skip_receiver_profile); // The method data pointer needs to be updated to reflect the new target. update_mdp_by_constant(mdp, in_bytes(VirtualCallData::virtual_call_data_size())); diff --git a/src/hotspot/cpu/arm/interp_masm_arm.hpp b/src/hotspot/cpu/arm/interp_masm_arm.hpp index 530be1c577e..147cd252b2c 100644 --- a/src/hotspot/cpu/arm/interp_masm_arm.hpp +++ b/src/hotspot/cpu/arm/interp_masm_arm.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2026, 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 @@ -239,8 +239,7 @@ class InterpreterMacroAssembler: public MacroAssembler { void profile_call(Register mdp); // Sets mdp, blows Rtemp. void profile_final_call(Register mdp); // Sets mdp, blows Rtemp. - void profile_virtual_call(Register mdp, Register receiver, // Sets mdp, blows Rtemp. - bool receiver_can_be_null = false); + void profile_virtual_call(Register mdp, Register receiver); // Sets mdp, blows Rtemp. void profile_ret(Register mdp, Register return_bci); // Sets mdp, blows R0-R3/R0-R18, Rtemp, LR void profile_null_seen(Register mdp); // Sets mdp. void profile_typecheck(Register mdp, Register klass); // Sets mdp, blows Rtemp. diff --git a/src/hotspot/cpu/arm/matcher_arm.hpp b/src/hotspot/cpu/arm/matcher_arm.hpp index 6c818e1f20d..7978a5b7090 100644 --- a/src/hotspot/cpu/arm/matcher_arm.hpp +++ b/src/hotspot/cpu/arm/matcher_arm.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -75,7 +75,6 @@ static bool narrow_klass_use_complex_address() { NOT_LP64(ShouldNotCallThis()); - assert(UseCompressedClassPointers, "only for compressed klass code"); return false; } diff --git a/src/hotspot/cpu/arm/methodHandles_arm.cpp b/src/hotspot/cpu/arm/methodHandles_arm.cpp index 3710fa33f36..2da14d8ffed 100644 --- a/src/hotspot/cpu/arm/methodHandles_arm.cpp +++ b/src/hotspot/cpu/arm/methodHandles_arm.cpp @@ -104,14 +104,13 @@ void MethodHandles::verify_ref_kind(MacroAssembler* _masm, int ref_kind, Registe __ andr(temp, temp, (unsigned)java_lang_invoke_MemberName::MN_REFERENCE_KIND_MASK); __ cmp(temp, ref_kind); __ b(L, eq); - { char* buf = NEW_C_HEAP_ARRAY(char, 100, mtInternal); - jio_snprintf(buf, 100, "verify_ref_kind expected %x", ref_kind); + const char* msg = ref_kind_to_verify_msg(ref_kind); if (ref_kind == JVM_REF_invokeVirtual || - ref_kind == JVM_REF_invokeSpecial) + ref_kind == JVM_REF_invokeSpecial) { // could do this for all ref_kinds, but would explode assembly code size - trace_method_handle(_masm, buf); - __ stop(buf); + trace_method_handle(_masm, msg); } + __ stop(msg); BLOCK_COMMENT("} verify_ref_kind"); __ bind(L); } diff --git a/src/hotspot/cpu/arm/stubDeclarations_arm.hpp b/src/hotspot/cpu/arm/stubDeclarations_arm.hpp index 5f768a205a5..5fb0d4e901f 100644 --- a/src/hotspot/cpu/arm/stubDeclarations_arm.hpp +++ b/src/hotspot/cpu/arm/stubDeclarations_arm.hpp @@ -29,7 +29,8 @@ #define STUBGEN_PREUNIVERSE_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(preuniverse, 500) \ do_stub(preuniverse, atomic_load_long) \ do_arch_entry(Arm, preuniverse, atomic_load_long, \ @@ -42,7 +43,8 @@ #define STUBGEN_INITIAL_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(initial, 9000) \ do_stub(initial, idiv_irem) \ do_arch_entry(Arm, initial, idiv_irem, \ @@ -51,14 +53,16 @@ #define STUBGEN_CONTINUATION_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(continuation, 2000) \ #define STUBGEN_COMPILER_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(compiler, 22000) \ do_stub(compiler, partial_subtype_check) \ do_arch_entry(Arm, compiler, partial_subtype_check, \ @@ -68,7 +72,8 @@ #define STUBGEN_FINAL_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(final, 22000) \ diff --git a/src/hotspot/cpu/arm/stubGenerator_arm.cpp b/src/hotspot/cpu/arm/stubGenerator_arm.cpp index a36ad3a0c47..a705b15eff5 100644 --- a/src/hotspot/cpu/arm/stubGenerator_arm.cpp +++ b/src/hotspot/cpu/arm/stubGenerator_arm.cpp @@ -3211,7 +3211,7 @@ class StubGenerator: public StubCodeGenerator { } public: - StubGenerator(CodeBuffer* code, BlobId blob_id) : StubCodeGenerator(code, blob_id) { + StubGenerator(CodeBuffer* code, BlobId blob_id, AOTStubData* stub_data) : StubCodeGenerator(code, blob_id, stub_data) { switch(blob_id) { case BlobId::stubgen_preuniverse_id: generate_preuniverse_stubs(); @@ -3235,8 +3235,8 @@ class StubGenerator: public StubCodeGenerator { } }; // end class declaration -void StubGenerator_generate(CodeBuffer* code, BlobId blob_id) { - StubGenerator g(code, blob_id); +void StubGenerator_generate(CodeBuffer* code, BlobId blob_id, AOTStubData* stub_data) { + StubGenerator g(code, blob_id, stub_data); } // implementation of internal development flag diff --git a/src/hotspot/cpu/arm/stubRoutines_arm.cpp b/src/hotspot/cpu/arm/stubRoutines_arm.cpp index a4f2b5e1bd9..38a9b298562 100644 --- a/src/hotspot/cpu/arm/stubRoutines_arm.cpp +++ b/src/hotspot/cpu/arm/stubRoutines_arm.cpp @@ -32,10 +32,16 @@ #define DEFINE_ARCH_ENTRY_INIT(arch, blob_name, stub_name, field_name, getter_name, init_function) \ address StubRoutines:: arch :: STUB_FIELD_NAME(field_name) = CAST_FROM_FN_PTR(address, init_function); -STUBGEN_ARCH_ENTRIES_DO(DEFINE_ARCH_ENTRY, DEFINE_ARCH_ENTRY_INIT) +STUBGEN_ARCH_ENTRIES_DO(DEFINE_ARCH_ENTRY, DEFINE_ARCH_ENTRY_INIT, DEFINE_ARCH_ENTRY_ARRAY) #undef DEFINE_ARCH_ENTRY_INIT #undef DEFINE_ARCH_ENTRY address StubRoutines::crc_table_addr() { ShouldNotCallThis(); return nullptr; } address StubRoutines::crc32c_table_addr() { ShouldNotCallThis(); return nullptr; } + +#if INCLUDE_CDS +// nothing to do for arm +void StubRoutines::init_AOTAddressTable() { +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/arm/stubRoutines_arm.hpp b/src/hotspot/cpu/arm/stubRoutines_arm.hpp index 45ab10d14f9..29d96d0e653 100644 --- a/src/hotspot/cpu/arm/stubRoutines_arm.hpp +++ b/src/hotspot/cpu/arm/stubRoutines_arm.hpp @@ -55,9 +55,13 @@ class Arm { #define DECLARE_ARCH_ENTRY_INIT(arch, blob_name, stub_name, field_name, getter_name, init_function) \ DECLARE_ARCH_ENTRY(arch, blob_name, stub_name, field_name, getter_name) -private: - STUBGEN_ARCH_ENTRIES_DO(DECLARE_ARCH_ENTRY, DECLARE_ARCH_ENTRY_INIT) +#define DECLARE_ARCH_ENTRY_ARRAY(arch, blob_name, stub_name, field_name, getter_name, count) \ + static address STUB_FIELD_NAME(field_name) [count] ; +private: + STUBGEN_ARCH_ENTRIES_DO(DECLARE_ARCH_ENTRY, DECLARE_ARCH_ENTRY_INIT, DECLARE_ARCH_ENTRY_ARRAY) + +#undef DECLARE_ARCH_ENTRY_ARRAY #undef DECLARE_ARCH_ENTRY_INIT #undef DECLARE_ARCH_ENTRY @@ -71,8 +75,12 @@ public: #define DEFINE_ARCH_ENTRY_GETTER_INIT(arch, blob_name, stub_name, field_name, getter_name, init_function) \ DEFINE_ARCH_ENTRY_GETTER(arch, blob_name, stub_name, field_name, getter_name) - STUBGEN_ARCH_ENTRIES_DO(DEFINE_ARCH_ENTRY_GETTER, DEFINE_ARCH_ENTRY_GETTER_INIT) +#define DEFINE_ARCH_ENTRY_GETTER_ARRAY(arch, blob_name, stub_name, field_name, getter_name, count) \ + static address getter_name(int idx) { return STUB_FIELD_NAME(field_name) [idx] ; } + STUBGEN_ARCH_ENTRIES_DO(DEFINE_ARCH_ENTRY_GETTER, DEFINE_ARCH_ENTRY_GETTER_INIT, DEFINE_ARCH_ENTRY_GETTER_ARRAY) + +#undef DEFINE_ARCH_ENTRY_GETTER_ARRAY #undef DEFINE_ARCH_ENTRY_GETTER_INIT #undef DEFINE_ARCH_ENTRY_GETTER diff --git a/src/hotspot/cpu/ppc/c1_LIRGenerator_ppc.cpp b/src/hotspot/cpu/ppc/c1_LIRGenerator_ppc.cpp index 5f030676bcb..a652a155f62 100644 --- a/src/hotspot/cpu/ppc/c1_LIRGenerator_ppc.cpp +++ b/src/hotspot/cpu/ppc/c1_LIRGenerator_ppc.cpp @@ -1143,6 +1143,7 @@ void LIRGenerator::volatile_field_load(LIR_Address* address, LIR_Opr result, Unimplemented(); // __ volatile_load_mem_reg(address, result, info); #endif + __ membar_acquire(); } diff --git a/src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp b/src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp index 798451446e5..4d7af0e4a71 100644 --- a/src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2025 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -144,7 +144,7 @@ void C1_MacroAssembler::initialize_header(Register obj, Register klass, Register if (len->is_valid()) { stw(len, arrayOopDesc::length_offset_in_bytes(), obj); - } else if (UseCompressedClassPointers && !UseCompactObjectHeaders) { + } else if (!UseCompactObjectHeaders) { // Otherwise length is in the class gap. store_klass_gap(obj); } diff --git a/src/hotspot/cpu/ppc/interp_masm_ppc.hpp b/src/hotspot/cpu/ppc/interp_masm_ppc.hpp index 4ea33ebaf63..275ff92c699 100644 --- a/src/hotspot/cpu/ppc/interp_masm_ppc.hpp +++ b/src/hotspot/cpu/ppc/interp_masm_ppc.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2025 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -258,7 +258,7 @@ class InterpreterMacroAssembler: public MacroAssembler { void profile_not_taken_branch(Register scratch1, Register scratch2); void profile_call(Register scratch1, Register scratch2); void profile_final_call(Register scratch1, Register scratch2); - void profile_virtual_call(Register Rreceiver, Register Rscratch1, Register Rscratch2, bool receiver_can_be_null); + void profile_virtual_call(Register Rreceiver, Register Rscratch1, Register Rscratch2); void profile_typecheck(Register Rklass, Register Rscratch1, Register Rscratch2); void profile_ret(TosState state, Register return_bci, Register scratch1, Register scratch2); void profile_switch_default(Register scratch1, Register scratch2); diff --git a/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp b/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp index f7bf457f72c..56eade8e533 100644 --- a/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp +++ b/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2025 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -1340,28 +1340,15 @@ void InterpreterMacroAssembler::profile_final_call(Register scratch1, Register s // Count a virtual call in the bytecodes. void InterpreterMacroAssembler::profile_virtual_call(Register Rreceiver, Register Rscratch1, - Register Rscratch2, - bool receiver_can_be_null) { + Register Rscratch2) { if (!ProfileInterpreter) { return; } Label profile_continue; // If no method data exists, go to profile_continue. test_method_data_pointer(profile_continue); - Label skip_receiver_profile; - if (receiver_can_be_null) { - Label not_null; - cmpdi(CR0, Rreceiver, 0); - bne(CR0, not_null); - // We are making a call. Increment the count for null receiver. - increment_mdp_data_at(in_bytes(CounterData::count_offset()), Rscratch1, Rscratch2); - b(skip_receiver_profile); - bind(not_null); - } - // Record the receiver type. record_klass_in_profile(Rreceiver, Rscratch1, Rscratch2); - bind(skip_receiver_profile); // The method data pointer needs to be updated to reflect the new target. update_mdp_by_constant(in_bytes(VirtualCallData::virtual_call_data_size())); diff --git a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp index 14e90ddf185..5fbcce94029 100644 --- a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp @@ -3201,23 +3201,17 @@ Register MacroAssembler::encode_klass_not_null(Register dst, Register src) { void MacroAssembler::store_klass(Register dst_oop, Register klass, Register ck) { assert(!UseCompactObjectHeaders, "not with compact headers"); - if (UseCompressedClassPointers) { - Register compressedKlass = encode_klass_not_null(ck, klass); - stw(compressedKlass, oopDesc::klass_offset_in_bytes(), dst_oop); - } else { - std(klass, oopDesc::klass_offset_in_bytes(), dst_oop); - } + Register compressedKlass = encode_klass_not_null(ck, klass); + stw(compressedKlass, oopDesc::klass_offset_in_bytes(), dst_oop); } void MacroAssembler::store_klass_gap(Register dst_oop, Register val) { assert(!UseCompactObjectHeaders, "not with compact headers"); - if (UseCompressedClassPointers) { - if (val == noreg) { - val = R0; - li(val, 0); - } - stw(val, oopDesc::klass_gap_offset_in_bytes(), dst_oop); + if (val == noreg) { + val = R0; + li(val, 0); } + stw(val, oopDesc::klass_gap_offset_in_bytes(), dst_oop); } int MacroAssembler::instr_size_for_decode_klass_not_null() { @@ -3226,17 +3220,13 @@ int MacroAssembler::instr_size_for_decode_klass_not_null() { // Not yet computed? if (computed_size == -1) { - if (!UseCompressedClassPointers) { - computed_size = 0; - } else { - // Determine by scratch emit. - ResourceMark rm; - int code_size = 8 * BytesPerInstWord; - CodeBuffer cb("decode_klass_not_null scratch buffer", code_size, 0); - MacroAssembler* a = new MacroAssembler(&cb); - a->decode_klass_not_null(R11_scratch1); - computed_size = a->offset(); - } + // Determine by scratch emit. + ResourceMark rm; + int code_size = 8 * BytesPerInstWord; + CodeBuffer cb("decode_klass_not_null scratch buffer", code_size, 0); + MacroAssembler* a = new MacroAssembler(&cb); + a->decode_klass_not_null(R11_scratch1); + computed_size = a->offset(); } return computed_size; @@ -3259,18 +3249,14 @@ void MacroAssembler::decode_klass_not_null(Register dst, Register src) { void MacroAssembler::load_klass_no_decode(Register dst, Register src) { if (UseCompactObjectHeaders) { load_narrow_klass_compact(dst, src); - } else if (UseCompressedClassPointers) { - lwz(dst, oopDesc::klass_offset_in_bytes(), src); } else { - ld(dst, oopDesc::klass_offset_in_bytes(), src); + lwz(dst, oopDesc::klass_offset_in_bytes(), src); } } void MacroAssembler::load_klass(Register dst, Register src) { load_klass_no_decode(dst, src); - if (UseCompressedClassPointers) { // also true for UseCompactObjectHeaders - decode_klass_not_null(dst); - } + decode_klass_not_null(dst); } // Loads the obj's Klass* into dst. @@ -3286,18 +3272,13 @@ void MacroAssembler::load_narrow_klass_compact(Register dst, Register src) { void MacroAssembler::cmp_klass(ConditionRegister dst, Register obj, Register klass, Register tmp, Register tmp2) { assert_different_registers(obj, klass, tmp); - if (UseCompressedClassPointers) { - if (UseCompactObjectHeaders) { - load_narrow_klass_compact(tmp, obj); - } else { - lwz(tmp, oopDesc::klass_offset_in_bytes(), obj); - } - Register encoded_klass = encode_klass_not_null(tmp2, klass); - cmpw(dst, tmp, encoded_klass); + if (UseCompactObjectHeaders) { + load_narrow_klass_compact(tmp, obj); } else { - ld(tmp, oopDesc::klass_offset_in_bytes(), obj); - cmpd(dst, tmp, klass); + lwz(tmp, oopDesc::klass_offset_in_bytes(), obj); } + Register encoded_klass = encode_klass_not_null(tmp2, klass); + cmpw(dst, tmp, encoded_klass); } void MacroAssembler::cmp_klasses_from_objects(ConditionRegister dst, Register obj1, Register obj2, Register tmp1, Register tmp2) { @@ -3305,14 +3286,10 @@ void MacroAssembler::cmp_klasses_from_objects(ConditionRegister dst, Register ob load_narrow_klass_compact(tmp1, obj1); load_narrow_klass_compact(tmp2, obj2); cmpw(dst, tmp1, tmp2); - } else if (UseCompressedClassPointers) { + } else { lwz(tmp1, oopDesc::klass_offset_in_bytes(), obj1); lwz(tmp2, oopDesc::klass_offset_in_bytes(), obj2); cmpw(dst, tmp1, tmp2); - } else { - ld(tmp1, oopDesc::klass_offset_in_bytes(), obj1); - ld(tmp2, oopDesc::klass_offset_in_bytes(), obj2); - cmpd(dst, tmp1, tmp2); } } diff --git a/src/hotspot/cpu/ppc/matcher_ppc.hpp b/src/hotspot/cpu/ppc/matcher_ppc.hpp index 2ddbec3e48c..cbe882648b8 100644 --- a/src/hotspot/cpu/ppc/matcher_ppc.hpp +++ b/src/hotspot/cpu/ppc/matcher_ppc.hpp @@ -87,7 +87,6 @@ static bool narrow_klass_use_complex_address() { NOT_LP64(ShouldNotCallThis()); - assert(UseCompressedClassPointers, "only for compressed klass code"); // TODO: PPC port if (MatchDecodeNodes) return true; return false; } diff --git a/src/hotspot/cpu/ppc/methodHandles_ppc.cpp b/src/hotspot/cpu/ppc/methodHandles_ppc.cpp index 45537e0ea96..ae94a9618b5 100644 --- a/src/hotspot/cpu/ppc/methodHandles_ppc.cpp +++ b/src/hotspot/cpu/ppc/methodHandles_ppc.cpp @@ -104,14 +104,13 @@ void MethodHandles::verify_ref_kind(MacroAssembler* _masm, int ref_kind, Registe __ andi(temp, temp, java_lang_invoke_MemberName::MN_REFERENCE_KIND_MASK); __ cmpwi(CR1, temp, ref_kind); __ beq(CR1, L); - { char* buf = NEW_C_HEAP_ARRAY(char, 100, mtInternal); - jio_snprintf(buf, 100, "verify_ref_kind expected %x", ref_kind); - if (ref_kind == JVM_REF_invokeVirtual || - ref_kind == JVM_REF_invokeSpecial) - // could do this for all ref_kinds, but would explode assembly code size - trace_method_handle(_masm, buf); - __ stop(buf); + const char* msg = ref_kind_to_verify_msg(ref_kind); + if (ref_kind == JVM_REF_invokeVirtual || + ref_kind == JVM_REF_invokeSpecial) { + // could do this for all ref_kinds, but would explode assembly code size + trace_method_handle(_masm, msg); } + __ stop(msg); BLOCK_COMMENT("} verify_ref_kind"); __ BIND(L); } diff --git a/src/hotspot/cpu/ppc/registerMap_ppc.cpp b/src/hotspot/cpu/ppc/registerMap_ppc.cpp new file mode 100644 index 00000000000..2e7f8af89d3 --- /dev/null +++ b/src/hotspot/cpu/ppc/registerMap_ppc.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026 SAP SE. 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 "runtime/registerMap.hpp" + +address RegisterMap::pd_location(VMReg base_reg, int slot_idx) const { + if (base_reg->is_VectorRegister()) { + // Not all physical slots belonging to a VectorRegister have corresponding + // valid VMReg locations in the RegisterMap. + // (See RegisterSaver::push_frame_reg_args_and_save_live_registers.) + // However, the slots are always saved to the stack in a contiguous region + // of memory so we can calculate the address of the upper slots by + // offsetting from the base address. + assert(base_reg->is_concrete(), "must pass base reg"); + address base_location = location(base_reg, nullptr); + if (base_location != nullptr) { + intptr_t offset_in_bytes = slot_idx * VMRegImpl::stack_slot_size; + return base_location + offset_in_bytes; + } else { + return nullptr; + } + } else { + return location(base_reg->next(slot_idx), nullptr); + } +} diff --git a/src/hotspot/cpu/ppc/registerMap_ppc.hpp b/src/hotspot/cpu/ppc/registerMap_ppc.hpp index 01eb642107c..607c712d10f 100644 --- a/src/hotspot/cpu/ppc/registerMap_ppc.hpp +++ b/src/hotspot/cpu/ppc/registerMap_ppc.hpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2013 SAP SE. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026 SAP SE. 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 @@ -35,9 +35,7 @@ // Since there is none, we just return null. address pd_location(VMReg reg) const { return nullptr; } - address pd_location(VMReg base_reg, int slot_idx) const { - return location(base_reg->next(slot_idx), nullptr); - } + address pd_location(VMReg base_reg, int slot_idx) const; // no PD state to clear or copy: void pd_clear() {} diff --git a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp index 5260ed978ff..53644210415 100644 --- a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp +++ b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp @@ -102,7 +102,7 @@ class RegisterSaver { // During deoptimization only the result registers need to be restored // all the other values have already been extracted. - static void restore_result_registers(MacroAssembler* masm, int frame_size_in_bytes); + static void restore_result_registers(MacroAssembler* masm, int frame_size_in_bytes, bool save_vectors); // Constants and data structures: @@ -349,7 +349,7 @@ OopMap* RegisterSaver::push_frame_reg_args_and_save_live_registers(MacroAssemble } // Note that generate_oop_map in the following loop is only used for the - // polling_page_vectors_safepoint_handler_blob. + // polling_page_vectors_safepoint_handler_blob and the deopt_blob. // The order in which the vector contents are stored depends on Endianess and // the utilized instructions (PowerArchitecturePPC64). assert(is_aligned(offset, StackAlignmentInBytes), "should be"); @@ -361,6 +361,7 @@ OopMap* RegisterSaver::push_frame_reg_args_and_save_live_registers(MacroAssemble __ stxvp(as_VectorRegister(reg_num).to_vsr(), offset, R1_SP); // Note: The contents were read in the same order (see loadV16_Power9 node in ppc.ad). + // RegisterMap::pd_location only uses the first VMReg for each VectorRegister. if (generate_oop_map) { map->set_callee_saved(VMRegImpl::stack2reg(offset >> 2), RegisterSaver_LiveVecRegs[i LITTLE_ENDIAN_ONLY(+1) ].vmreg); @@ -380,6 +381,7 @@ OopMap* RegisterSaver::push_frame_reg_args_and_save_live_registers(MacroAssemble __ stxvd2x(as_VectorRegister(reg_num)->to_vsr(), R31, R1_SP); } // Note: The contents were read in the same order (see loadV16_Power8 / loadV16_Power9 node in ppc.ad). + // RegisterMap::pd_location only uses the first VMReg for each VectorRegister. if (generate_oop_map) { VMReg vsr = RegisterSaver_LiveVecRegs[i].vmreg; map->set_callee_saved(VMRegImpl::stack2reg(offset >> 2), vsr); @@ -566,10 +568,14 @@ void RegisterSaver::restore_argument_registers_and_pop_frame(MacroAssembler*masm } // Restore the registers that might be holding a result. -void RegisterSaver::restore_result_registers(MacroAssembler* masm, int frame_size_in_bytes) { +void RegisterSaver::restore_result_registers(MacroAssembler* masm, int frame_size_in_bytes, bool save_vectors) { const int regstosave_num = sizeof(RegisterSaver_LiveRegs) / sizeof(RegisterSaver::LiveRegType); - const int register_save_size = regstosave_num * reg_size; // VS registers not relevant here. + const int vecregstosave_num = save_vectors ? (sizeof(RegisterSaver_LiveVecRegs) / + sizeof(RegisterSaver::LiveRegType)) + : 0; + const int register_save_size = regstosave_num * reg_size + vecregstosave_num * vec_reg_size; + const int register_save_offset = frame_size_in_bytes - register_save_size; // restore all result registers (ints and floats) @@ -598,7 +604,7 @@ void RegisterSaver::restore_result_registers(MacroAssembler* masm, int frame_siz offset += reg_size; } - assert(offset == frame_size_in_bytes, "consistency check"); + assert(offset == frame_size_in_bytes - (save_vectors ? vecregstosave_num * vec_reg_size : 0), "consistency check"); } // Is vector's size (in bytes) bigger than a size saved by default? @@ -2909,7 +2915,8 @@ void SharedRuntime::generate_deopt_blob() { map = RegisterSaver::push_frame_reg_args_and_save_live_registers(masm, &first_frame_size_in_bytes, /*generate_oop_map=*/ true, - RegisterSaver::return_pc_is_lr); + RegisterSaver::return_pc_is_lr, + /*save_vectors*/ SuperwordUseVSX); assert(map != nullptr, "OopMap must have been created"); __ li(exec_mode_reg, Deoptimization::Unpack_deopt); @@ -2943,7 +2950,8 @@ void SharedRuntime::generate_deopt_blob() { RegisterSaver::push_frame_reg_args_and_save_live_registers(masm, &first_frame_size_in_bytes, /*generate_oop_map=*/ false, - RegisterSaver::return_pc_is_pre_saved); + RegisterSaver::return_pc_is_pre_saved, + /*save_vectors*/ SuperwordUseVSX); // Deopt during an exception. Save exec mode for unpack_frames. __ li(exec_mode_reg, Deoptimization::Unpack_exception); @@ -2958,7 +2966,8 @@ void SharedRuntime::generate_deopt_blob() { RegisterSaver::push_frame_reg_args_and_save_live_registers(masm, &first_frame_size_in_bytes, /*generate_oop_map=*/ false, - RegisterSaver::return_pc_is_pre_saved); + RegisterSaver::return_pc_is_pre_saved, + /*save_vectors*/ SuperwordUseVSX); __ li(exec_mode_reg, Deoptimization::Unpack_reexecute); #endif @@ -2984,7 +2993,7 @@ void SharedRuntime::generate_deopt_blob() { // Restore only the result registers that have been saved // by save_volatile_registers(...). - RegisterSaver::restore_result_registers(masm, first_frame_size_in_bytes); + RegisterSaver::restore_result_registers(masm, first_frame_size_in_bytes, /*save_vectors*/ SuperwordUseVSX); // reload the exec mode from the UnrollBlock (it might have changed) __ lwz(exec_mode_reg, in_bytes(Deoptimization::UnrollBlock::unpack_kind_offset()), unroll_block_reg); diff --git a/src/hotspot/cpu/ppc/stubDeclarations_ppc.hpp b/src/hotspot/cpu/ppc/stubDeclarations_ppc.hpp index be51afe42a4..41b8b71486d 100644 --- a/src/hotspot/cpu/ppc/stubDeclarations_ppc.hpp +++ b/src/hotspot/cpu/ppc/stubDeclarations_ppc.hpp @@ -29,35 +29,40 @@ #define STUBGEN_PREUNIVERSE_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(preuniverse, 0) \ #define STUBGEN_INITIAL_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(initial, 20000) \ #define STUBGEN_CONTINUATION_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(continuation, 2000) \ #define STUBGEN_COMPILER_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(compiler, 24000) \ #define STUBGEN_FINAL_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(final, 24000) \ diff --git a/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp b/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp index e48778a8b9f..f528587a8bb 100644 --- a/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp +++ b/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp @@ -5095,7 +5095,7 @@ void generate_lookup_secondary_supers_table_stub() { } public: - StubGenerator(CodeBuffer* code, BlobId blob_id) : StubCodeGenerator(code, blob_id) { + StubGenerator(CodeBuffer* code, BlobId blob_id, AOTStubData *stub_data) : StubCodeGenerator(code, blob_id, stub_data) { switch(blob_id) { case BlobId::stubgen_preuniverse_id: generate_preuniverse_stubs(); @@ -5119,7 +5119,7 @@ void generate_lookup_secondary_supers_table_stub() { } }; -void StubGenerator_generate(CodeBuffer* code, BlobId blob_id) { - StubGenerator g(code, blob_id); +void StubGenerator_generate(CodeBuffer* code, BlobId blob_id, AOTStubData *stub_data) { + StubGenerator g(code, blob_id, stub_data); } diff --git a/src/hotspot/cpu/ppc/stubRoutines_ppc_64.cpp b/src/hotspot/cpu/ppc/stubRoutines_ppc_64.cpp index 914c5a17a19..3b7ee66348a 100644 --- a/src/hotspot/cpu/ppc/stubRoutines_ppc_64.cpp +++ b/src/hotspot/cpu/ppc/stubRoutines_ppc_64.cpp @@ -183,3 +183,9 @@ address StubRoutines::ppc::generate_crc_constants(juint reverse_poly) { return consts; } + +#if INCLUDE_CDS +// nothing to do for ppc +void StubRoutines::init_AOTAddressTable() { +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp b/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp index 8a3af748fa1..37f780535b4 100644 --- a/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp +++ b/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp @@ -3489,7 +3489,7 @@ void TemplateTable::invokevirtual(int byte_no) { // Get receiver klass. __ load_klass_check_null_throw(Rrecv_klass, Rrecv, R11_scratch1); __ verify_klass_ptr(Rrecv_klass); - __ profile_virtual_call(Rrecv_klass, R11_scratch1, R12_scratch2, false); + __ profile_virtual_call(Rrecv_klass, R11_scratch1, R12_scratch2); generate_vtable_call(Rrecv_klass, Rvtableindex_or_method, Rret_addr, R11_scratch1); } @@ -3596,7 +3596,7 @@ void TemplateTable::invokeinterface_object_method(Register Rrecv_klass, // Non-final callc case. __ bind(LnotFinal); __ lhz(Rindex, in_bytes(ResolvedMethodEntry::table_index_offset()), Rcache); - __ profile_virtual_call(Rrecv_klass, Rtemp1, Rscratch, false); + __ profile_virtual_call(Rrecv_klass, Rtemp1, Rscratch); generate_vtable_call(Rrecv_klass, Rindex, Rret, Rscratch); } @@ -3664,7 +3664,7 @@ void TemplateTable::invokeinterface(int byte_no) { __ lookup_interface_method(Rrecv_klass, Rinterface_klass, noreg, noreg, Rscratch1, Rscratch2, L_no_such_interface, /*return_method=*/false); - __ profile_virtual_call(Rrecv_klass, Rscratch1, Rscratch2, false); + __ profile_virtual_call(Rrecv_klass, Rscratch1, Rscratch2); // Find entry point to call. diff --git a/src/hotspot/cpu/ppc/vm_version_ppc.cpp b/src/hotspot/cpu/ppc/vm_version_ppc.cpp index e471f5a6e4f..3e3b1103c86 100644 --- a/src/hotspot/cpu/ppc/vm_version_ppc.cpp +++ b/src/hotspot/cpu/ppc/vm_version_ppc.cpp @@ -25,6 +25,7 @@ #include "asm/assembler.inline.hpp" #include "asm/macroAssembler.inline.hpp" +#include "compiler/compilerDefinitions.inline.hpp" #include "compiler/disassembler.hpp" #include "jvm.h" #include "memory/resourceArea.hpp" @@ -105,7 +106,7 @@ void VM_Version::initialize() { if (PowerArchitecturePPC64 >= 9) { // Performance is good since Power9. - if (FLAG_IS_DEFAULT(SuperwordUseVSX)) { + if (FLAG_IS_DEFAULT(SuperwordUseVSX) && CompilerConfig::is_c2_enabled()) { FLAG_SET_ERGO(SuperwordUseVSX, true); } } @@ -310,11 +311,6 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UseSHA3Intrinsics, false); } - if (!(UseSHA1Intrinsics || UseSHA256Intrinsics || UseSHA512Intrinsics)) { - FLAG_SET_DEFAULT(UseSHA, false); - } - - #ifdef COMPILER2 if (FLAG_IS_DEFAULT(UseSquareToLenIntrinsic)) { UseSquareToLenIntrinsic = true; diff --git a/src/hotspot/cpu/riscv/c1_LIRAssembler_arraycopy_riscv.cpp b/src/hotspot/cpu/riscv/c1_LIRAssembler_arraycopy_riscv.cpp index 819d6c05654..8aced227a06 100644 --- a/src/hotspot/cpu/riscv/c1_LIRAssembler_arraycopy_riscv.cpp +++ b/src/hotspot/cpu/riscv/c1_LIRAssembler_arraycopy_riscv.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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. * @@ -196,12 +196,9 @@ void LIR_Assembler::arraycopy_type_check(Register src, Register src_pos, Registe if (UseCompactObjectHeaders) { __ load_narrow_klass_compact(tmp, src); __ load_narrow_klass_compact(t0, dst); - } else if (UseCompressedClassPointers) { + } else { __ lwu(tmp, Address(src, oopDesc::klass_offset_in_bytes())); __ lwu(t0, Address(dst, oopDesc::klass_offset_in_bytes())); - } else { - __ ld(tmp, Address(src, oopDesc::klass_offset_in_bytes())); - __ ld(t0, Address(dst, oopDesc::klass_offset_in_bytes())); } __ bne(tmp, t0, *stub->entry(), /* is_far */ true); } else { @@ -243,37 +240,6 @@ void LIR_Assembler::arraycopy_type_check(Register src, Register src_pos, Registe } } -void LIR_Assembler::arraycopy_assert(Register src, Register dst, Register tmp, ciArrayKlass *default_type, int flags) { - assert(default_type != nullptr, "null default_type!"); - BasicType basic_type = default_type->element_type()->basic_type(); - if (basic_type == T_ARRAY) { basic_type = T_OBJECT; } - if (basic_type != T_OBJECT || !(flags & LIR_OpArrayCopy::type_check)) { - // Sanity check the known type with the incoming class. For the - // primitive case the types must match exactly with src.klass and - // dst.klass each exactly matching the default type. For the - // object array case, if no type check is needed then either the - // dst type is exactly the expected type and the src type is a - // subtype which we can't check or src is the same array as dst - // but not necessarily exactly of type default_type. - Label known_ok, halt; - __ mov_metadata(tmp, default_type->constant_encoding()); - if (UseCompressedClassPointers) { - __ encode_klass_not_null(tmp); - } - - if (basic_type != T_OBJECT) { - __ cmp_klass_compressed(dst, tmp, t0, halt, false); - __ cmp_klass_compressed(src, tmp, t0, known_ok, true); - } else { - __ cmp_klass_compressed(dst, tmp, t0, known_ok, true); - __ beq(src, dst, known_ok); - } - __ bind(halt); - __ stop("incorrect type information in arraycopy"); - __ bind(known_ok); - } -} - void LIR_Assembler::emit_arraycopy(LIR_OpArrayCopy* op) { ciArrayKlass *default_type = op->expected_type(); Register src = op->src()->as_register(); @@ -304,7 +270,28 @@ void LIR_Assembler::emit_arraycopy(LIR_OpArrayCopy* op) { } #ifdef ASSERT - arraycopy_assert(src, dst, tmp, default_type, flags); + if (basic_type != T_OBJECT || !(flags & LIR_OpArrayCopy::type_check)) { + // Sanity check the known type with the incoming class. For the + // primitive case the types must match exactly with src.klass and + // dst.klass each exactly matching the default type. For the + // object array case, if no type check is needed then either the + // dst type is exactly the expected type and the src type is a + // subtype which we can't check or src is the same array as dst + // but not necessarily exactly of type default_type. + Label known_ok, halt; + __ mov_metadata(tmp, default_type->constant_encoding()); + + if (basic_type != T_OBJECT) { + __ cmp_klass_bne(dst, tmp, t0, t1, halt); + __ cmp_klass_beq(src, tmp, t0, t1, known_ok); + } else { + __ cmp_klass_beq(dst, tmp, t0, t1, known_ok); + __ beq(src, dst, known_ok); + } + __ bind(halt); + __ stop("incorrect type information in arraycopy"); + __ bind(known_ok); + } #endif #ifndef PRODUCT diff --git a/src/hotspot/cpu/riscv/c1_LIRAssembler_arraycopy_riscv.hpp b/src/hotspot/cpu/riscv/c1_LIRAssembler_arraycopy_riscv.hpp index 06a0f248ca6..b5452f3e4cd 100644 --- a/src/hotspot/cpu/riscv/c1_LIRAssembler_arraycopy_riscv.hpp +++ b/src/hotspot/cpu/riscv/c1_LIRAssembler_arraycopy_riscv.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2021, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -39,7 +39,6 @@ void arraycopy_type_check(Register src, Register src_pos, Register length, Register dst, Register dst_pos, Register tmp, CodeStub *stub, BasicType basic_type, int flags); - void arraycopy_assert(Register src, Register dst, Register tmp, ciArrayKlass *default_type, int flags); void arraycopy_prepare_params(Register src, Register src_pos, Register length, Register dst, Register dst_pos, BasicType basic_type); void arraycopy_checkcast_prepare_params(Register src, Register src_pos, Register length, diff --git a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp index 63e2fd015d7..29e5d86d0cc 100644 --- a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2020, 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. @@ -55,20 +55,6 @@ const Register SHIFT_count = x10; // where count for shift operations must be #define __ _masm-> -static void select_different_registers(Register preserve, - Register extra, - Register &tmp1, - Register &tmp2) { - if (tmp1 == preserve) { - assert_different_registers(tmp1, tmp2, extra); - tmp1 = extra; - } else if (tmp2 == preserve) { - assert_different_registers(tmp1, tmp2, extra); - tmp2 = extra; - } - assert_different_registers(preserve, tmp1, tmp2); -} - static void select_different_registers(Register preserve, Register extra, Register &tmp1, @@ -1155,12 +1141,8 @@ void LIR_Assembler::emit_typecheck_helper(LIR_OpTypeCheck *op, Label* success, L } else if (obj == klass_RInfo) { klass_RInfo = dst; } - if (k->is_loaded() && !UseCompressedClassPointers) { - select_different_registers(obj, dst, k_RInfo, klass_RInfo); - } else { - Rtmp1 = op->tmp3()->as_register(); - select_different_registers(obj, dst, k_RInfo, klass_RInfo, Rtmp1); - } + Rtmp1 = op->tmp3()->as_register(); + select_different_registers(obj, dst, k_RInfo, klass_RInfo, Rtmp1); assert_different_registers(obj, k_RInfo, klass_RInfo); diff --git a/src/hotspot/cpu/riscv/c1_LIRGenerator_riscv.cpp b/src/hotspot/cpu/riscv/c1_LIRGenerator_riscv.cpp index 88565d9136f..5e0deb84a14 100644 --- a/src/hotspot/cpu/riscv/c1_LIRGenerator_riscv.cpp +++ b/src/hotspot/cpu/riscv/c1_LIRGenerator_riscv.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 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. @@ -1073,9 +1073,7 @@ void LIRGenerator::do_CheckCast(CheckCast* x) { } LIR_Opr reg = rlock_result(x); LIR_Opr tmp3 = LIR_OprFact::illegalOpr; - if (!x->klass()->is_loaded() || UseCompressedClassPointers) { - tmp3 = new_register(objectType); - } + tmp3 = new_register(objectType); __ checkcast(reg, obj.result(), x->klass(), new_register(objectType), new_register(objectType), tmp3, x->direct_compare(), info_for_exception, patching_info, stub, @@ -1094,9 +1092,7 @@ void LIRGenerator::do_InstanceOf(InstanceOf* x) { } obj.load_item(); LIR_Opr tmp3 = LIR_OprFact::illegalOpr; - if (!x->klass()->is_loaded() || UseCompressedClassPointers) { - tmp3 = new_register(objectType); - } + tmp3 = new_register(objectType); __ instanceof(reg, obj.result(), x->klass(), new_register(objectType), new_register(objectType), tmp3, x->direct_compare(), patching_info, x->profiled_method(), x->profiled_bci()); @@ -1173,4 +1169,5 @@ void LIRGenerator::volatile_field_store(LIR_Opr value, LIR_Address* address, void LIRGenerator::volatile_field_load(LIR_Address* address, LIR_Opr result, CodeEmitInfo* info) { __ volatile_load_mem_reg(address, result, info); + __ membar_acquire(); } diff --git a/src/hotspot/cpu/riscv/c1_MacroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c1_MacroAssembler_riscv.cpp index aeb077ba0a0..abcc070b253 100644 --- a/src/hotspot/cpu/riscv/c1_MacroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/c1_MacroAssembler_riscv.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 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. @@ -92,12 +92,8 @@ void C1_MacroAssembler::initialize_header(Register obj, Register klass, Register // This assumes that all prototype bits fitr in an int32_t mv(tmp1, checked_cast(markWord::prototype().value())); sd(tmp1, Address(obj, oopDesc::mark_offset_in_bytes())); - if (UseCompressedClassPointers) { // Take care not to kill klass - encode_klass_not_null(tmp1, klass, tmp2); - sw(tmp1, Address(obj, oopDesc::klass_offset_in_bytes())); - } else { - sd(klass, Address(obj, oopDesc::klass_offset_in_bytes())); - } + encode_klass_not_null(tmp1, klass, tmp2); + sw(tmp1, Address(obj, oopDesc::klass_offset_in_bytes())); } if (len->is_valid()) { @@ -108,7 +104,7 @@ void C1_MacroAssembler::initialize_header(Register obj, Register klass, Register // Clear gap/first 4 bytes following the length field. sw(zr, Address(obj, base_offset)); } - } else if (UseCompressedClassPointers && !UseCompactObjectHeaders) { + } else if (!UseCompactObjectHeaders) { store_klass_gap(obj, zr); } } diff --git a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp index 72a90ddde1f..c9cd8220551 100644 --- a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp @@ -1175,8 +1175,7 @@ void C2_MacroAssembler::string_compare_long_same_encoding(Register result, Regis Label TAIL_CHECK, TAIL, NEXT_WORD, DIFFERENCE; const int base_offset = arrayOopDesc::base_offset_in_bytes(T_BYTE); - assert((base_offset % (UseCompactObjectHeaders ? 4 : - (UseCompressedClassPointers ? 8 : 4))) == 0, "Must be"); + assert((base_offset % (UseCompactObjectHeaders ? 4 : 8)) == 0, "Must be"); const int minCharsInWord = isLL ? wordSize : wordSize / 2; @@ -1269,8 +1268,7 @@ void C2_MacroAssembler::string_compare_long_different_encoding(Register result, Label TAIL, NEXT_WORD, DIFFERENCE; const int base_offset = arrayOopDesc::base_offset_in_bytes(T_BYTE); - assert((base_offset % (UseCompactObjectHeaders ? 4 : - (UseCompressedClassPointers ? 8 : 4))) == 0, "Must be"); + assert((base_offset % (UseCompactObjectHeaders ? 4 : 8)) == 0, "Must be"); Register strL = isLU ? str1 : str2; Register strU = isLU ? str2 : str1; @@ -1485,8 +1483,7 @@ void C2_MacroAssembler::arrays_equals(Register a1, Register a2, int length_offset = arrayOopDesc::length_offset_in_bytes(); int base_offset = arrayOopDesc::base_offset_in_bytes(elem_size == 2 ? T_CHAR : T_BYTE); - assert((base_offset % (UseCompactObjectHeaders ? 4 : - (UseCompressedClassPointers ? 8 : 4))) == 0, "Must be"); + assert((base_offset % (UseCompactObjectHeaders ? 4 : 8)) == 0, "Must be"); Register cnt1 = tmp3; Register cnt2 = tmp1; // cnt2 only used in array length compare @@ -1611,8 +1608,7 @@ void C2_MacroAssembler::string_equals(Register a1, Register a2, int base_offset = arrayOopDesc::base_offset_in_bytes(T_BYTE); - assert((base_offset % (UseCompactObjectHeaders ? 4 : - (UseCompressedClassPointers ? 8 : 4))) == 0, "Must be"); + assert((base_offset % (UseCompactObjectHeaders ? 4 : 8)) == 0, "Must be"); BLOCK_COMMENT("string_equals {"); @@ -2699,8 +2695,7 @@ void C2_MacroAssembler::arrays_equals_v(Register a1, Register a2, Register resul int length_offset = arrayOopDesc::length_offset_in_bytes(); int base_offset = arrayOopDesc::base_offset_in_bytes(elem_size == 2 ? T_CHAR : T_BYTE); - assert((base_offset % (UseCompactObjectHeaders ? 4 : - (UseCompressedClassPointers ? 8 : 4))) == 0, "Must be"); + assert((base_offset % (UseCompactObjectHeaders ? 4 : 8)) == 0, "Must be"); BLOCK_COMMENT("arrays_equals_v {"); @@ -3074,12 +3069,12 @@ void C2_MacroAssembler::reduce_mul_integral_v(Register dst, Register src1, Vecto // If the operation is MUL, then the identity value is one. vmv_v_i(vtmp1, 1); vmerge_vvm(vtmp2, vtmp1, src2); // vm == v0 - vslidedown_vi(vtmp1, vtmp2, vector_length); + slidedown_v(vtmp1, vtmp2, vector_length); vsetvli_helper(bt, vector_length); vmul_vv(vtmp1, vtmp1, vtmp2); } else { - vslidedown_vi(vtmp1, src2, vector_length); + slidedown_v(vtmp1, src2, vector_length); vsetvli_helper(bt, vector_length); vmul_vv(vtmp1, vtmp1, src2); @@ -3087,7 +3082,7 @@ void C2_MacroAssembler::reduce_mul_integral_v(Register dst, Register src1, Vecto while (vector_length > 1) { vector_length /= 2; - vslidedown_vi(vtmp2, vtmp1, vector_length); + slidedown_v(vtmp2, vtmp1, vector_length); vsetvli_helper(bt, vector_length); vmul_vv(vtmp1, vtmp1, vtmp2); } @@ -3286,40 +3281,44 @@ VFCVT_SAFE(vfcvt_rtz_x_f_v); // Extract a scalar element from an vector at position 'idx'. // The input elements in src are expected to be of integral type. -void C2_MacroAssembler::extract_v(Register dst, VectorRegister src, BasicType bt, - int idx, VectorRegister tmp) { +void C2_MacroAssembler::extract_v(Register dst, VectorRegister src, + BasicType bt, int idx, VectorRegister vtmp) { assert(is_integral_type(bt), "unsupported element type"); assert(idx >= 0, "idx cannot be negative"); // Only need the first element after vector slidedown vsetvli_helper(bt, 1); if (idx == 0) { vmv_x_s(dst, src); - } else if (idx <= 31) { - vslidedown_vi(tmp, src, idx); - vmv_x_s(dst, tmp); } else { - mv(t0, idx); - vslidedown_vx(tmp, src, t0); - vmv_x_s(dst, tmp); + slidedown_v(vtmp, src, idx); + vmv_x_s(dst, vtmp); } } // Extract a scalar element from an vector at position 'idx'. // The input elements in src are expected to be of floating point type. -void C2_MacroAssembler::extract_fp_v(FloatRegister dst, VectorRegister src, BasicType bt, - int idx, VectorRegister tmp) { +void C2_MacroAssembler::extract_fp_v(FloatRegister dst, VectorRegister src, + BasicType bt, int idx, VectorRegister vtmp) { assert(is_floating_point_type(bt), "unsupported element type"); assert(idx >= 0, "idx cannot be negative"); // Only need the first element after vector slidedown vsetvli_helper(bt, 1); if (idx == 0) { vfmv_f_s(dst, src); - } else if (idx <= 31) { - vslidedown_vi(tmp, src, idx); - vfmv_f_s(dst, tmp); } else { - mv(t0, idx); - vslidedown_vx(tmp, src, t0); - vfmv_f_s(dst, tmp); + slidedown_v(vtmp, src, idx); + vfmv_f_s(dst, vtmp); + } +} + +// Move elements down a vector register group. +// Offset is the start index (offset) for the source. +void C2_MacroAssembler::slidedown_v(VectorRegister dst, VectorRegister src, + uint32_t offset, Register tmp) { + if (is_uimm5(offset)) { + vslidedown_vi(dst, src, offset); + } else { + mv(tmp, offset); + vslidedown_vx(dst, src, tmp); } } diff --git a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.hpp b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.hpp index fa87ceba295..468d53b1a54 100644 --- a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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. * @@ -296,7 +296,13 @@ void vfcvt_rtz_x_f_v_safe(VectorRegister dst, VectorRegister src); - void extract_v(Register dst, VectorRegister src, BasicType bt, int idx, VectorRegister tmp); - void extract_fp_v(FloatRegister dst, VectorRegister src, BasicType bt, int idx, VectorRegister tmp); + void extract_v(Register dst, VectorRegister src, + BasicType bt, int idx, VectorRegister vtmp); + + void extract_fp_v(FloatRegister dst, VectorRegister src, + BasicType bt, int idx, VectorRegister vtmp); + + void slidedown_v(VectorRegister dst, VectorRegister src, + uint32_t offset, Register tmp = t0); #endif // CPU_RISCV_C2_MACROASSEMBLER_RISCV_HPP diff --git a/src/hotspot/cpu/riscv/gc/shared/cardTableBarrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/shared/cardTableBarrierSetAssembler_riscv.cpp index d94bf428fd2..9eb546a1888 100644 --- a/src/hotspot/cpu/riscv/gc/shared/cardTableBarrierSetAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/shared/cardTableBarrierSetAssembler_riscv.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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. * @@ -56,8 +56,10 @@ void CardTableBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet d } } -void CardTableBarrierSetAssembler::store_check(MacroAssembler* masm, Register obj, Register tmp) { - assert_different_registers(obj, tmp); +void CardTableBarrierSetAssembler::store_check(MacroAssembler* masm, Register obj, Register tmp1, Register tmp2) { + precond(tmp1 != noreg); + precond(tmp2 != noreg); + assert_different_registers(obj, tmp1, tmp2); BarrierSet* bs = BarrierSet::barrier_set(); assert(bs->kind() == BarrierSet::CardTableBarrierSet, "Wrong barrier set kind"); @@ -65,17 +67,17 @@ void CardTableBarrierSetAssembler::store_check(MacroAssembler* masm, Register ob assert(CardTable::dirty_card_val() == 0, "must be"); - __ load_byte_map_base(tmp); - __ add(tmp, obj, tmp); + __ load_byte_map_base(tmp1); + __ add(tmp1, obj, tmp1); if (UseCondCardMark) { Label L_already_dirty; - __ lbu(t1, Address(tmp)); - __ beqz(t1, L_already_dirty); - __ sb(zr, Address(tmp)); + __ lbu(tmp2, Address(tmp1)); + __ beqz(tmp2, L_already_dirty); + __ sb(zr, Address(tmp1)); __ bind(L_already_dirty); } else { - __ sb(zr, Address(tmp)); + __ sb(zr, Address(tmp1)); } } @@ -119,10 +121,10 @@ void CardTableBarrierSetAssembler::oop_store_at(MacroAssembler* masm, DecoratorS if (needs_post_barrier) { // flatten object address if needed if (!precise || dst.offset() == 0) { - store_check(masm, dst.base(), tmp3); + store_check(masm, dst.base(), tmp1, tmp2); } else { __ la(tmp3, dst); - store_check(masm, tmp3, t0); + store_check(masm, tmp3, tmp1, tmp2); } } } diff --git a/src/hotspot/cpu/riscv/gc/shared/cardTableBarrierSetAssembler_riscv.hpp b/src/hotspot/cpu/riscv/gc/shared/cardTableBarrierSetAssembler_riscv.hpp index 6f6e9065103..1576f0a6dd8 100644 --- a/src/hotspot/cpu/riscv/gc/shared/cardTableBarrierSetAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/gc/shared/cardTableBarrierSetAssembler_riscv.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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. * @@ -31,7 +31,7 @@ class CardTableBarrierSetAssembler: public BarrierSetAssembler { protected: - void store_check(MacroAssembler* masm, Register obj, Register tmp); + void store_check(MacroAssembler* masm, Register obj, Register tmp1, Register tmp2); virtual void gen_write_ref_array_pre_barrier(MacroAssembler* masm, DecoratorSet decorators, Register addr, Register count, RegSet saved_regs) {} diff --git a/src/hotspot/cpu/riscv/interp_masm_riscv.cpp b/src/hotspot/cpu/riscv/interp_masm_riscv.cpp index 744590bec2b..804c2072ba5 100644 --- a/src/hotspot/cpu/riscv/interp_masm_riscv.cpp +++ b/src/hotspot/cpu/riscv/interp_masm_riscv.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2020, 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. @@ -1040,26 +1040,15 @@ void InterpreterMacroAssembler::profile_final_call(Register mdp) { void InterpreterMacroAssembler::profile_virtual_call(Register receiver, - Register mdp, - bool receiver_can_be_null) { + Register mdp) { if (ProfileInterpreter) { Label profile_continue; // If no method data exists, go to profile_continue. test_method_data_pointer(mdp, profile_continue); - Label skip_receiver_profile; - if (receiver_can_be_null) { - Label not_null; - // We are making a call. Increment the count for null receiver. - increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset())); - j(skip_receiver_profile); - bind(not_null); - } - // Record the receiver type. profile_receiver_type(receiver, mdp, 0); - bind(skip_receiver_profile); // The method data pointer needs to be updated to reflect the new target. diff --git a/src/hotspot/cpu/riscv/interp_masm_riscv.hpp b/src/hotspot/cpu/riscv/interp_masm_riscv.hpp index 59cc76b022f..df86f0dc532 100644 --- a/src/hotspot/cpu/riscv/interp_masm_riscv.hpp +++ b/src/hotspot/cpu/riscv/interp_masm_riscv.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2015, Red Hat Inc. All rights reserved. * Copyright (c) 2020, 2021, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -274,8 +274,7 @@ class InterpreterMacroAssembler: public MacroAssembler { void profile_not_taken_branch(Register mdp); void profile_call(Register mdp); void profile_final_call(Register mdp); - void profile_virtual_call(Register receiver, Register mdp, - bool receiver_can_be_null = false); + void profile_virtual_call(Register receiver, Register mdp); void profile_ret(Register return_bci, Register mdp); void profile_null_seen(Register mdp); void profile_typecheck(Register mdp, Register klass); diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp index 4f5e7afc166..0e32c602d95 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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. @@ -49,6 +49,7 @@ #include "runtime/sharedRuntime.hpp" #include "runtime/stubRoutines.hpp" #include "utilities/globalDefinitions.hpp" +#include "utilities/integerCast.hpp" #include "utilities/powerOfTwo.hpp" #ifdef COMPILER2 #include "opto/compile.hpp" @@ -1947,14 +1948,12 @@ void MacroAssembler::restore_cpu_control_state_after_jni(Register tmp) { } } -void MacroAssembler::push_reg(Register Rs) -{ +void MacroAssembler::push_reg(Register Rs) { subi(esp, esp, wordSize); sd(Rs, Address(esp, 0)); } -void MacroAssembler::pop_reg(Register Rd) -{ +void MacroAssembler::pop_reg(Register Rd) { ld(Rd, Address(esp, 0)); addi(esp, esp, wordSize); } @@ -1973,7 +1972,11 @@ int MacroAssembler::bitset_to_regs(unsigned int bitset, unsigned char* regs) { // Push integer registers in the bitset supplied. Don't push sp. // Return the number of words pushed -int MacroAssembler::push_reg(unsigned int bitset, Register stack) { +int MacroAssembler::push_reg(RegSet regset, Register stack) { + if (regset.bits() == 0) { + return 0; + } + auto bitset = integer_cast(regset.bits()); DEBUG_ONLY(int words_pushed = 0;) unsigned char regs[32]; int count = bitset_to_regs(bitset, regs); @@ -1993,7 +1996,11 @@ int MacroAssembler::push_reg(unsigned int bitset, Register stack) { return count; } -int MacroAssembler::pop_reg(unsigned int bitset, Register stack) { +int MacroAssembler::pop_reg(RegSet regset, Register stack) { + if (regset.bits() == 0) { + return 0; + } + auto bitset = integer_cast(regset.bits()); DEBUG_ONLY(int words_popped = 0;) unsigned char regs[32]; int count = bitset_to_regs(bitset, regs); @@ -2015,7 +2022,11 @@ int MacroAssembler::pop_reg(unsigned int bitset, Register stack) { // Push floating-point registers in the bitset supplied. // Return the number of words pushed -int MacroAssembler::push_fp(unsigned int bitset, Register stack) { +int MacroAssembler::push_fp(FloatRegSet regset, Register stack) { + if (regset.bits() == 0) { + return 0; + } + auto bitset = integer_cast(regset.bits()); DEBUG_ONLY(int words_pushed = 0;) unsigned char regs[32]; int count = bitset_to_regs(bitset, regs); @@ -2035,7 +2046,11 @@ int MacroAssembler::push_fp(unsigned int bitset, Register stack) { return count; } -int MacroAssembler::pop_fp(unsigned int bitset, Register stack) { +int MacroAssembler::pop_fp(FloatRegSet regset, Register stack) { + if (regset.bits() == 0) { + return 0; + } + auto bitset = integer_cast(regset.bits()); DEBUG_ONLY(int words_popped = 0;) unsigned char regs[32]; int count = bitset_to_regs(bitset, regs); @@ -2721,7 +2736,11 @@ void MacroAssembler::kernel_crc32(Register crc, Register buf, Register len, #ifdef COMPILER2 // Push vector registers in the bitset supplied. // Return the number of words pushed -int MacroAssembler::push_v(unsigned int bitset, Register stack) { +int MacroAssembler::push_v(VectorRegSet regset, Register stack) { + if (regset.bits() == 0) { + return 0; + } + auto bitset = integer_cast(regset.bits()); int vector_size_in_bytes = Matcher::scalable_vector_reg_size(T_BYTE); // Scan bitset to accumulate register pairs @@ -2736,7 +2755,11 @@ int MacroAssembler::push_v(unsigned int bitset, Register stack) { return count * vector_size_in_bytes / wordSize; } -int MacroAssembler::pop_v(unsigned int bitset, Register stack) { +int MacroAssembler::pop_v(VectorRegSet regset, Register stack) { + if (regset.bits() == 0) { + return 0; + } + auto bitset = integer_cast(regset.bits()); int vector_size_in_bytes = Matcher::scalable_vector_reg_size(T_BYTE); // Scan bitset to accumulate register pairs @@ -3511,19 +3534,30 @@ void MacroAssembler::orptr(Address adr, RegisterOrConstant src, Register tmp1, R sd(tmp1, adr); } -void MacroAssembler::cmp_klass_compressed(Register oop, Register trial_klass, Register tmp, Label &L, bool equal) { +void MacroAssembler::cmp_klass_beq(Register obj, Register klass, + Register tmp1, Register tmp2, + Label &L, bool is_far) { + assert_different_registers(obj, klass, tmp1, tmp2); if (UseCompactObjectHeaders) { - load_narrow_klass_compact(tmp, oop); - } else if (UseCompressedClassPointers) { - lwu(tmp, Address(oop, oopDesc::klass_offset_in_bytes())); + load_narrow_klass_compact(tmp1, obj); } else { - ld(tmp, Address(oop, oopDesc::klass_offset_in_bytes())); + lwu(tmp1, Address(obj, oopDesc::klass_offset_in_bytes())); } - if (equal) { - beq(trial_klass, tmp, L); + decode_klass_not_null(tmp1, tmp2); + beq(klass, tmp1, L, is_far); +} + +void MacroAssembler::cmp_klass_bne(Register obj, Register klass, + Register tmp1, Register tmp2, + Label &L, bool is_far) { + assert_different_registers(obj, klass, tmp1, tmp2); + if (UseCompactObjectHeaders) { + load_narrow_klass_compact(tmp1, obj); } else { - bne(trial_klass, tmp, L); + lwu(tmp1, Address(obj, oopDesc::klass_offset_in_bytes())); } + decode_klass_not_null(tmp1, tmp2); + bne(klass, tmp1, L, is_far); } // Move an oop into a register. @@ -3741,11 +3775,9 @@ void MacroAssembler::load_klass(Register dst, Register src, Register tmp) { if (UseCompactObjectHeaders) { load_narrow_klass_compact(dst, src); decode_klass_not_null(dst, tmp); - } else if (UseCompressedClassPointers) { + } else { lwu(dst, Address(src, oopDesc::klass_offset_in_bytes())); decode_klass_not_null(dst, tmp); - } else { - ld(dst, Address(src, oopDesc::klass_offset_in_bytes())); } } @@ -3753,20 +3785,15 @@ void MacroAssembler::store_klass(Register dst, Register src, Register tmp) { // FIXME: Should this be a store release? concurrent gcs assumes // klass length is valid if klass field is not null. assert(!UseCompactObjectHeaders, "not with compact headers"); - if (UseCompressedClassPointers) { - encode_klass_not_null(src, tmp); - sw(src, Address(dst, oopDesc::klass_offset_in_bytes())); - } else { - sd(src, Address(dst, oopDesc::klass_offset_in_bytes())); - } + encode_klass_not_null(src, tmp); + sw(src, Address(dst, oopDesc::klass_offset_in_bytes())); + } void MacroAssembler::store_klass_gap(Register dst, Register src) { assert(!UseCompactObjectHeaders, "not with compact headers"); - if (UseCompressedClassPointers) { - // Store to klass gap in destination - sw(src, Address(dst, oopDesc::klass_gap_offset_in_bytes())); - } + // Store to klass gap in destination + sw(src, Address(dst, oopDesc::klass_gap_offset_in_bytes())); } void MacroAssembler::decode_klass_not_null(Register r, Register tmp) { @@ -3775,7 +3802,6 @@ void MacroAssembler::decode_klass_not_null(Register r, Register tmp) { } void MacroAssembler::decode_klass_not_null(Register dst, Register src, Register tmp) { - assert(UseCompressedClassPointers, "should only be used for compressed headers"); assert_different_registers(dst, tmp); assert_different_registers(src, tmp); @@ -3806,8 +3832,6 @@ void MacroAssembler::encode_klass_not_null(Register r, Register tmp) { } void MacroAssembler::encode_klass_not_null(Register dst, Register src, Register tmp) { - assert(UseCompressedClassPointers, "should only be used for compressed headers"); - if (CompressedKlassPointers::base() == nullptr) { if (CompressedKlassPointers::shift() != 0) { srli(dst, src, CompressedKlassPointers::shift()); @@ -5337,7 +5361,6 @@ void MacroAssembler::set_narrow_oop(Register dst, jobject obj) { } void MacroAssembler::set_narrow_klass(Register dst, Klass* k) { - assert (UseCompressedClassPointers, "should only be used for compressed headers"); assert (oop_recorder() != nullptr, "this assembler needs an OopRecorder"); int index = oop_recorder()->find_index(k); @@ -5417,12 +5440,9 @@ int MacroAssembler::ic_check(int end_alignment) { if (UseCompactObjectHeaders) { load_narrow_klass_compact(tmp1, receiver); lwu(tmp2, Address(data, CompiledICData::speculated_klass_offset())); - } else if (UseCompressedClassPointers) { + } else { lwu(tmp1, Address(receiver, oopDesc::klass_offset_in_bytes())); lwu(tmp2, Address(data, CompiledICData::speculated_klass_offset())); - } else { - ld(tmp1, Address(receiver, oopDesc::klass_offset_in_bytes())); - ld(tmp2, Address(data, CompiledICData::speculated_klass_offset())); } Label ic_hit; @@ -5543,13 +5563,6 @@ void MacroAssembler::decrementw(const Address dst, int32_t value, Register tmp1, sw(tmp1, adr); } -void MacroAssembler::cmpptr(Register src1, const Address &src2, Label& equal, Register tmp) { - assert_different_registers(src1, tmp); - assert(src2.getMode() == Address::literal, "must be applied to a literal address"); - ld(tmp, src2); - beq(src1, tmp, equal); -} - void MacroAssembler::load_method_holder_cld(Register result, Register method) { load_method_holder(result, method); ld(result, Address(result, InstanceKlass::class_loader_data_offset())); diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp index f5e985c28a2..4cc55e7ae23 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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. @@ -198,7 +198,12 @@ class MacroAssembler: public Assembler { void load_klass(Register dst, Register src, Register tmp = t0); void load_narrow_klass_compact(Register dst, Register src); void store_klass(Register dst, Register src, Register tmp = t0); - void cmp_klass_compressed(Register oop, Register trial_klass, Register tmp, Label &L, bool equal); + void cmp_klass_beq(Register obj, Register klass, + Register tmp1, Register tmp2, + Label &L, bool is_far = false); + void cmp_klass_bne(Register obj, Register klass, + Register tmp1, Register tmp2, + Label &L, bool is_far = false); void encode_klass_not_null(Register r, Register tmp = t0); void decode_klass_not_null(Register r, Register tmp = t0); @@ -813,15 +818,6 @@ class MacroAssembler: public Assembler { void double_bgt(FloatRegister Rs1, FloatRegister Rs2, Label &l, bool is_far = false, bool is_unordered = false); private: - int push_reg(unsigned int bitset, Register stack); - int pop_reg(unsigned int bitset, Register stack); - int push_fp(unsigned int bitset, Register stack); - int pop_fp(unsigned int bitset, Register stack); -#ifdef COMPILER2 - int push_v(unsigned int bitset, Register stack); - int pop_v(unsigned int bitset, Register stack); -#endif // COMPILER2 - // The signed 20-bit upper imm can materialize at most negative 0xF...F80000000, two G. // The following signed 12-bit imm can at max subtract 0x800, two K, from that previously loaded two G. bool is_valid_32bit_offset(int64_t x) { @@ -839,15 +835,19 @@ private: } public: + // Stack push and pop individual 64 bit registers void push_reg(Register Rs); void pop_reg(Register Rd); - void push_reg(RegSet regs, Register stack) { if (regs.bits()) push_reg(regs.bits(), stack); } - void pop_reg(RegSet regs, Register stack) { if (regs.bits()) pop_reg(regs.bits(), stack); } - void push_fp(FloatRegSet regs, Register stack) { if (regs.bits()) push_fp(regs.bits(), stack); } - void pop_fp(FloatRegSet regs, Register stack) { if (regs.bits()) pop_fp(regs.bits(), stack); } + + int push_reg(RegSet regset, Register stack); + int pop_reg(RegSet regset, Register stack); + + int push_fp(FloatRegSet regset, Register stack); + int pop_fp(FloatRegSet regset, Register stack); + #ifdef COMPILER2 - void push_v(VectorRegSet regs, Register stack) { if (regs.bits()) push_v(regs.bits(), stack); } - void pop_v(VectorRegSet regs, Register stack) { if (regs.bits()) pop_v(regs.bits(), stack); } + int push_v(VectorRegSet regset, Register stack); + int pop_v(VectorRegSet regset, Register stack); #endif // COMPILER2 // Push and pop everything that might be clobbered by a native @@ -1348,9 +1348,8 @@ public: void decrement(const Address dst, int64_t value = 1, Register tmp1 = t0, Register tmp2 = t1); void decrementw(const Address dst, int32_t value = 1, Register tmp1 = t0, Register tmp2 = t1); - void cmpptr(Register src1, const Address &src2, Label& equal, Register tmp = t0); - void clinit_barrier(Register klass, Register tmp, Label* L_fast_path = nullptr, Label* L_slow_path = nullptr); + void load_method_holder_cld(Register result, Register method); void load_method_holder(Register holder, Register method); diff --git a/src/hotspot/cpu/riscv/methodHandles_riscv.cpp b/src/hotspot/cpu/riscv/methodHandles_riscv.cpp index d770999df96..e80dedf58ed 100644 --- a/src/hotspot/cpu/riscv/methodHandles_riscv.cpp +++ b/src/hotspot/cpu/riscv/methodHandles_riscv.cpp @@ -72,17 +72,22 @@ void MethodHandles::verify_klass(MacroAssembler* _masm, InstanceKlass** klass_addr = vmClasses::klass_addr_at(klass_id); Klass* klass = vmClasses::klass_at(klass_id); Register temp1 = t1; - Register temp2 = t0; // used by MacroAssembler::cmpptr + Register temp2 = t0; Label L_ok, L_bad; BLOCK_COMMENT("verify_klass {"); __ verify_oop(obj); __ beqz(obj, L_bad); + __ push_reg(RegSet::of(temp1, temp2), sp); __ load_klass(temp1, obj, temp2); - __ cmpptr(temp1, ExternalAddress((address) klass_addr), L_ok); + __ ld(temp2, ExternalAddress((address)klass_addr)); + __ beq(temp1, temp2, L_ok); + intptr_t super_check_offset = klass->super_check_offset(); __ ld(temp1, Address(temp1, super_check_offset)); - __ cmpptr(temp1, ExternalAddress((address) klass_addr), L_ok); + __ ld(temp2, ExternalAddress((address)klass_addr)); + __ beq(temp1, temp2, L_ok); + __ pop_reg(RegSet::of(temp1, temp2), sp); __ bind(L_bad); __ stop(error_message); diff --git a/src/hotspot/cpu/riscv/riscv.ad b/src/hotspot/cpu/riscv/riscv.ad index 54c0d9c0955..e236d03e6d2 100644 --- a/src/hotspot/cpu/riscv/riscv.ad +++ b/src/hotspot/cpu/riscv/riscv.ad @@ -1801,13 +1801,8 @@ void MachUEPNode::format(PhaseRegAlloc* ra_, outputStream* st) const { assert_cond(st != nullptr); st->print_cr("# MachUEPNode"); - if (UseCompressedClassPointers) { - st->print_cr("\tlwu t1, [j_rarg0 + oopDesc::klass_offset_in_bytes()]\t# compressed klass"); - st->print_cr("\tlwu t2, [t0 + CompiledICData::speculated_klass_offset()]\t# compressed klass"); - } else { - st->print_cr("\tld t1, [j_rarg0 + oopDesc::klass_offset_in_bytes()]\t# compressed klass"); - st->print_cr("\tld t2, [t0 + CompiledICData::speculated_klass_offset()]\t# compressed klass"); - } + st->print_cr("\tlwu t1, [j_rarg0 + oopDesc::klass_offset_in_bytes()]\t# compressed klass"); + st->print_cr("\tlwu t2, [t0 + CompiledICData::speculated_klass_offset()]\t# compressed klass"); st->print_cr("\tbeq t1, t2, ic_hit"); st->print_cr("\tj, SharedRuntime::_ic_miss_stub\t # Inline cache check"); st->print_cr("\tic_hit:"); diff --git a/src/hotspot/cpu/riscv/stubDeclarations_riscv.hpp b/src/hotspot/cpu/riscv/stubDeclarations_riscv.hpp index f977d759d20..890e354fd27 100644 --- a/src/hotspot/cpu/riscv/stubDeclarations_riscv.hpp +++ b/src/hotspot/cpu/riscv/stubDeclarations_riscv.hpp @@ -29,28 +29,32 @@ #define STUBGEN_PREUNIVERSE_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(preuniverse, 0) \ #define STUBGEN_INITIAL_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(initial, 10000) \ #define STUBGEN_CONTINUATION_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(continuation, 2000) \ #define STUBGEN_COMPILER_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(compiler, 45000) \ do_stub(compiler, compare_long_string_LL) \ do_arch_entry(riscv, compiler, compare_long_string_LL, \ @@ -81,7 +85,8 @@ #define STUBGEN_FINAL_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(final, 20000 ZGC_ONLY(+10000)) \ do_stub(final, copy_byte_f) \ do_arch_entry(riscv, final, copy_byte_f, copy_byte_f, \ diff --git a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp index 127ac9f6951..4656b5c0d41 100644 --- a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp +++ b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2025, Red Hat Inc. All rights reserved. * Copyright (c) 2020, 2025, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -3070,8 +3070,7 @@ class StubGenerator: public StubCodeGenerator { const Register tmp = x30, tmpLval = x12; int base_offset = arrayOopDesc::base_offset_in_bytes(T_BYTE); - assert((base_offset % (UseCompactObjectHeaders ? 4 : - (UseCompressedClassPointers ? 8 : 4))) == 0, "Must be"); + assert((base_offset % (UseCompactObjectHeaders ? 4 : 8)) == 0, "Must be"); #ifdef ASSERT if (AvoidUnalignedAccesses) { @@ -3128,8 +3127,7 @@ class StubGenerator: public StubCodeGenerator { tmp1 = x28, tmp2 = x29, tmp3 = x30, tmp4 = x12; int base_offset = arrayOopDesc::base_offset_in_bytes(T_BYTE); - assert((base_offset % (UseCompactObjectHeaders ? 4 : - (UseCompressedClassPointers ? 8 : 4))) == 0, "Must be"); + assert((base_offset % (UseCompactObjectHeaders ? 4 : 8)) == 0, "Must be"); Register strU = isLU ? str2 : str1, strL = isLU ? str1 : str2, @@ -7350,7 +7348,7 @@ static const int64_t right_3_bits = right_n_bits(3); } public: - StubGenerator(CodeBuffer* code, BlobId blob_id) : StubCodeGenerator(code, blob_id) { + StubGenerator(CodeBuffer* code, BlobId blob_id, AOTStubData* stub_data) : StubCodeGenerator(code, blob_id, stub_data) { switch(blob_id) { case BlobId::stubgen_preuniverse_id: generate_preuniverse_stubs(); @@ -7374,6 +7372,6 @@ static const int64_t right_3_bits = right_n_bits(3); } }; // end class declaration -void StubGenerator_generate(CodeBuffer* code, BlobId blob_id) { - StubGenerator g(code, blob_id); +void StubGenerator_generate(CodeBuffer* code, BlobId blob_id, AOTStubData* stub_data) { + StubGenerator g(code, blob_id, stub_data); } diff --git a/src/hotspot/cpu/riscv/stubRoutines_riscv.cpp b/src/hotspot/cpu/riscv/stubRoutines_riscv.cpp index 2aac95d71fa..b7f69eff9fa 100644 --- a/src/hotspot/cpu/riscv/stubRoutines_riscv.cpp +++ b/src/hotspot/cpu/riscv/stubRoutines_riscv.cpp @@ -42,8 +42,12 @@ #define DEFINE_ARCH_ENTRY_INIT(arch, blob_name, stub_name, field_name, getter_name, init_function) \ address StubRoutines:: arch :: STUB_FIELD_NAME(field_name) = CAST_FROM_FN_PTR(address, init_function); -STUBGEN_ARCH_ENTRIES_DO(DEFINE_ARCH_ENTRY, DEFINE_ARCH_ENTRY_INIT) +#define DEFINE_ARCH_ENTRY_ARRAY(arch, blob_name, stub_name, field_name, getter_name, count) \ + address StubRoutines:: arch :: STUB_FIELD_NAME(field_name) [count] ; +STUBGEN_ARCH_ENTRIES_DO(DEFINE_ARCH_ENTRY, DEFINE_ARCH_ENTRY_INIT, DEFINE_ARCH_ENTRY_ARRAY) + +#undef DEFINE_ARCH_ENTRY_ARRAY #undef DEFINE_ARCH_ENTRY_INIT #undef DEFINE_ARCH_ENTRY @@ -501,3 +505,9 @@ ATTRIBUTE_ALIGNED(4096) juint StubRoutines::riscv::_crc_table[] = 0x751997d0UL, 0x00000001UL, 0xccaa009eUL, 0x00000000UL, }; + +#if INCLUDE_CDS +// nothing to do for riscv +void StubRoutines::init_AOTAddressTable() { +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/riscv/stubRoutines_riscv.hpp b/src/hotspot/cpu/riscv/stubRoutines_riscv.hpp index 2c4e7210413..ec67a338052 100644 --- a/src/hotspot/cpu/riscv/stubRoutines_riscv.hpp +++ b/src/hotspot/cpu/riscv/stubRoutines_riscv.hpp @@ -61,9 +61,13 @@ class riscv { #define DECLARE_ARCH_ENTRY_INIT(arch, blob_name, stub_name, field_name, getter_name, init_function) \ DECLARE_ARCH_ENTRY(arch, blob_name, stub_name, field_name, getter_name) -private: - STUBGEN_ARCH_ENTRIES_DO(DECLARE_ARCH_ENTRY, DECLARE_ARCH_ENTRY_INIT) +#define DECLARE_ARCH_ENTRY_ARRAY(arch, blob_name, stub_name, field_name, getter_name, count) \ + static address STUB_FIELD_NAME(field_name) [count] ; +private: + STUBGEN_ARCH_ENTRIES_DO(DECLARE_ARCH_ENTRY, DECLARE_ARCH_ENTRY_INIT, DECLARE_ARCH_ENTRY_ARRAY) + +#undef DECLARE_ARCH_ENTRY_ARRAY #undef DECLARE_ARCH_ENTRY_INIT #undef DECLARE_ARCH_ENTRY @@ -79,8 +83,12 @@ private: #define DEFINE_ARCH_ENTRY_GETTER_INIT(arch, blob_name, stub_name, field_name, getter_name, init_function) \ DEFINE_ARCH_ENTRY_GETTER(arch, blob_name, stub_name, field_name, getter_name) - STUBGEN_ARCH_ENTRIES_DO(DEFINE_ARCH_ENTRY_GETTER, DEFINE_ARCH_ENTRY_GETTER_INIT) +#define DEFINE_ARCH_ENTRY_GETTER_ARRAY(arch, blob_name, stub_name, field_name, getter_name, count) \ + static address getter_name(int idx) { return STUB_FIELD_NAME(field_name) [idx] ; } + STUBGEN_ARCH_ENTRIES_DO(DEFINE_ARCH_ENTRY_GETTER, DEFINE_ARCH_ENTRY_GETTER_INIT, DEFINE_ARCH_ENTRY_GETTER_ARRAY) + +#undef DEFINE_ARCH_ENTRY_GETTER_ARRAY #undef DEFINE_ARCH_ENTRY_GETTER_INIT #undef DEFINE_ARCH_ENTRY_GETTER diff --git a/src/hotspot/cpu/riscv/vm_version_riscv.cpp b/src/hotspot/cpu/riscv/vm_version_riscv.cpp index 36f0864da0b..3a6415d52bd 100644 --- a/src/hotspot/cpu/riscv/vm_version_riscv.cpp +++ b/src/hotspot/cpu/riscv/vm_version_riscv.cpp @@ -420,11 +420,6 @@ void VM_Version::c2_initialize() { FLAG_SET_DEFAULT(UseSHA3Intrinsics, false); } - // UseSHA - if (!(UseSHA1Intrinsics || UseSHA256Intrinsics || UseSHA3Intrinsics || UseSHA512Intrinsics)) { - FLAG_SET_DEFAULT(UseSHA, false); - } - // AES if (UseZvkn) { UseAES = UseAES || FLAG_IS_DEFAULT(UseAES); diff --git a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp index 93d6051aa76..e1d8d062c23 100644 --- a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2024 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -2251,9 +2251,7 @@ void LIR_Assembler::emit_arraycopy(LIR_OpArrayCopy* op) { // but not necessarily exactly of type default_type. NearLabel known_ok, halt; metadata2reg(default_type->constant_encoding(), tmp); - if (UseCompressedClassPointers) { - __ encode_klass_not_null(tmp); - } + __ encode_klass_not_null(tmp); if (basic_type != T_OBJECT) { __ cmp_klass(tmp, dst, Z_R1_scratch); @@ -2540,13 +2538,8 @@ void LIR_Assembler::emit_typecheck_helper(LIR_OpTypeCheck *op, Label* success, L // Get object class. // Not a safepoint as obj null check happens earlier. if (op->fast_check()) { - if (UseCompressedClassPointers) { - __ load_klass(klass_RInfo, obj); - __ compareU64_and_branch(k_RInfo, klass_RInfo, Assembler::bcondNotEqual, *failure_target); - } else { - __ z_cg(k_RInfo, Address(obj, oopDesc::klass_offset_in_bytes())); - __ branch_optimized(Assembler::bcondNotEqual, *failure_target); - } + __ load_klass(klass_RInfo, obj); + __ compareU64_and_branch(k_RInfo, klass_RInfo, Assembler::bcondNotEqual, *failure_target); // Successful cast, fall through to profile or jump. } else { bool need_slow_path = !k->is_loaded() || diff --git a/src/hotspot/cpu/s390/c1_LIRGenerator_s390.cpp b/src/hotspot/cpu/s390/c1_LIRGenerator_s390.cpp index 5a0fd5f9561..1ffd172df8f 100644 --- a/src/hotspot/cpu/s390/c1_LIRGenerator_s390.cpp +++ b/src/hotspot/cpu/s390/c1_LIRGenerator_s390.cpp @@ -1046,6 +1046,7 @@ void LIRGenerator::volatile_field_store(LIR_Opr value, LIR_Address* address, void LIRGenerator::volatile_field_load(LIR_Address* address, LIR_Opr result, CodeEmitInfo* info) { __ load(address, result, info); + __ membar_acquire(); } void LIRGenerator::do_update_CRC32(Intrinsic* x) { diff --git a/src/hotspot/cpu/s390/c1_MacroAssembler_s390.cpp b/src/hotspot/cpu/s390/c1_MacroAssembler_s390.cpp index 993c1a1b552..813143938f9 100644 --- a/src/hotspot/cpu/s390/c1_MacroAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/c1_MacroAssembler_s390.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2024 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -107,10 +107,10 @@ void C1_MacroAssembler::initialize_header(Register obj, Register klass, Register } if (len->is_valid()) { - // Length will be in the klass gap, if one exists. + // Length will be in the klass gap. z_st(len, Address(obj, arrayOopDesc::length_offset_in_bytes())); - } else if (UseCompressedClassPointers && !UseCompactObjectHeaders) { - store_klass_gap(Rzero, obj); // Zero klass gap for compressed oops. + } else if (!UseCompactObjectHeaders) { + store_klass_gap(Rzero, obj); // Zero klass gap. } } diff --git a/src/hotspot/cpu/s390/interp_masm_s390.cpp b/src/hotspot/cpu/s390/interp_masm_s390.cpp index a80ca26239b..d5239898dd7 100644 --- a/src/hotspot/cpu/s390/interp_masm_s390.cpp +++ b/src/hotspot/cpu/s390/interp_masm_s390.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2024 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -1259,27 +1259,15 @@ void InterpreterMacroAssembler::profile_final_call(Register mdp) { void InterpreterMacroAssembler::profile_virtual_call(Register receiver, Register mdp, - Register reg2, - bool receiver_can_be_null) { + Register reg2) { if (ProfileInterpreter) { NearLabel profile_continue; // If no method data exists, go to profile_continue. test_method_data_pointer(mdp, profile_continue); - NearLabel skip_receiver_profile; - if (receiver_can_be_null) { - NearLabel not_null; - compareU64_and_branch(receiver, (intptr_t)0L, bcondNotEqual, not_null); - // We are making a call. Increment the count for null receiver. - increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset())); - z_bru(skip_receiver_profile); - bind(not_null); - } - // Record the receiver type. record_klass_in_profile(receiver, mdp, reg2); - bind(skip_receiver_profile); // The method data pointer needs to be updated to reflect the new target. update_mdp_by_constant(mdp, in_bytes(VirtualCallData::virtual_call_data_size())); diff --git a/src/hotspot/cpu/s390/interp_masm_s390.hpp b/src/hotspot/cpu/s390/interp_masm_s390.hpp index d981f9ea01e..b816185b065 100644 --- a/src/hotspot/cpu/s390/interp_masm_s390.hpp +++ b/src/hotspot/cpu/s390/interp_masm_s390.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2024 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -296,8 +296,7 @@ class InterpreterMacroAssembler: public MacroAssembler { void profile_call(Register mdp); void profile_final_call(Register mdp); void profile_virtual_call(Register receiver, Register mdp, - Register scratch2, - bool receiver_can_be_null = false); + Register scratch2); void profile_ret(Register return_bci, Register mdp); void profile_null_seen(Register mdp); void profile_typecheck(Register mdp, Register klass, Register scratch); diff --git a/src/hotspot/cpu/s390/macroAssembler_s390.cpp b/src/hotspot/cpu/s390/macroAssembler_s390.cpp index 6e132f895bd..de3608e74ba 100644 --- a/src/hotspot/cpu/s390/macroAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/macroAssembler_s390.cpp @@ -1237,7 +1237,6 @@ void MacroAssembler::load_narrow_oop(Register t, narrowOop a) { // Load narrow klass constant, compression required. void MacroAssembler::load_narrow_klass(Register t, Klass* k) { - assert(UseCompressedClassPointers, "must be on to call this method"); narrowKlass encoded_k = CompressedKlassPointers::encode(k); load_const_32to64(t, encoded_k, false /*sign_extend*/); } @@ -1255,7 +1254,6 @@ void MacroAssembler::compare_immediate_narrow_oop(Register oop1, narrowOop oop2) // Compare narrow oop in reg with narrow oop constant, no decompression. void MacroAssembler::compare_immediate_narrow_klass(Register klass1, Klass* klass2) { - assert(UseCompressedClassPointers, "must be on to call this method"); narrowKlass encoded_k = CompressedKlassPointers::encode(klass2); Assembler::z_clfi(klass1, encoded_k); @@ -1348,8 +1346,6 @@ int MacroAssembler::patch_load_narrow_oop(address pos, oop o) { // Patching the immediate value of CPU version dependent load_narrow_klass sequence. // The passed ptr must NOT be in compressed format! int MacroAssembler::patch_load_narrow_klass(address pos, Klass* k) { - assert(UseCompressedClassPointers, "Can only patch compressed klass pointers"); - narrowKlass nk = CompressedKlassPointers::encode(k); return patch_load_const_32to64(pos, nk); } @@ -1364,8 +1360,6 @@ int MacroAssembler::patch_compare_immediate_narrow_oop(address pos, oop o) { // Patching the immediate value of CPU version dependent compare_immediate_narrow_klass sequence. // The passed ptr must NOT be in compressed format! int MacroAssembler::patch_compare_immediate_narrow_klass(address pos, Klass* k) { - assert(UseCompressedClassPointers, "Can only patch compressed klass pointers"); - narrowKlass nk = CompressedKlassPointers::encode(k); return patch_compare_immediate_32(pos, nk); } @@ -2235,10 +2229,8 @@ int MacroAssembler::ic_check(int end_alignment) { if (UseCompactObjectHeaders) { load_narrow_klass_compact(R1_scratch, R2_receiver); - } else if (UseCompressedClassPointers) { - z_llgf(R1_scratch, Address(R2_receiver, oopDesc::klass_offset_in_bytes())); } else { - z_lg(R1_scratch, Address(R2_receiver, oopDesc::klass_offset_in_bytes())); + z_llgf(R1_scratch, Address(R2_receiver, oopDesc::klass_offset_in_bytes())); } z_cg(R1_scratch, Address(R9_data, in_bytes(CompiledICData::speculated_klass_offset()))); z_bre(success); @@ -3916,7 +3908,6 @@ void MacroAssembler::encode_klass_not_null(Register dst, Register src) { address base = CompressedKlassPointers::base(); int shift = CompressedKlassPointers::shift(); bool need_zero_extend = base != nullptr; - assert(UseCompressedClassPointers, "only for compressed klass ptrs"); BLOCK_COMMENT("cKlass encoder {"); @@ -4013,7 +4004,6 @@ int MacroAssembler::instr_size_for_decode_klass_not_null() { address base = CompressedKlassPointers::base(); int shift_size = CompressedKlassPointers::shift() == 0 ? 0 : 6; /* sllg */ int addbase_size = 0; - assert(UseCompressedClassPointers, "only for compressed klass ptrs"); if (base != nullptr) { unsigned int base_h = ((unsigned long)base)>>32; @@ -4043,7 +4033,6 @@ void MacroAssembler::decode_klass_not_null(Register dst) { address base = CompressedKlassPointers::base(); int shift = CompressedKlassPointers::shift(); int beg_off = offset(); - assert(UseCompressedClassPointers, "only for compressed klass ptrs"); BLOCK_COMMENT("cKlass decoder (const size) {"); @@ -4085,7 +4074,6 @@ void MacroAssembler::decode_klass_not_null(Register dst) { void MacroAssembler::decode_klass_not_null(Register dst, Register src) { address base = CompressedKlassPointers::base(); int shift = CompressedKlassPointers::shift(); - assert(UseCompressedClassPointers, "only for compressed klass ptrs"); BLOCK_COMMENT("cKlass decoder {"); @@ -4125,13 +4113,9 @@ void MacroAssembler::decode_klass_not_null(Register dst, Register src) { } void MacroAssembler::load_klass(Register klass, Address mem) { - if (UseCompressedClassPointers) { - z_llgf(klass, mem); - // Attention: no null check here! - decode_klass_not_null(klass); - } else { - z_lg(klass, mem); - } + z_llgf(klass, mem); + // Attention: no null check here! + decode_klass_not_null(klass); } // Loads the obj's Klass* into dst. @@ -4154,10 +4138,8 @@ void MacroAssembler::cmp_klass(Register klass, Register obj, Register tmp) { assert_different_registers(klass, obj, tmp); load_narrow_klass_compact(tmp, obj); z_cr(klass, tmp); - } else if (UseCompressedClassPointers) { - z_c(klass, Address(obj, oopDesc::klass_offset_in_bytes())); } else { - z_cg(klass, Address(obj, oopDesc::klass_offset_in_bytes())); + z_c(klass, Address(obj, oopDesc::klass_offset_in_bytes())); } BLOCK_COMMENT("} cmp_klass"); } @@ -4170,12 +4152,9 @@ void MacroAssembler::cmp_klasses_from_objects(Register obj1, Register obj2, Regi load_narrow_klass_compact(tmp1, obj1); load_narrow_klass_compact(tmp2, obj2); z_cr(tmp1, tmp2); - } else if (UseCompressedClassPointers) { + } else { z_l(tmp1, Address(obj1, oopDesc::klass_offset_in_bytes())); z_c(tmp1, Address(obj2, oopDesc::klass_offset_in_bytes())); - } else { - z_lg(tmp1, Address(obj1, oopDesc::klass_offset_in_bytes())); - z_cg(tmp1, Address(obj2, oopDesc::klass_offset_in_bytes())); } BLOCK_COMMENT("} cmp_klasses_from_objects"); } @@ -4184,36 +4163,28 @@ void MacroAssembler::load_klass(Register klass, Register src_oop) { if (UseCompactObjectHeaders) { load_narrow_klass_compact(klass, src_oop); decode_klass_not_null(klass); - } else if (UseCompressedClassPointers) { + } else { z_llgf(klass, oopDesc::klass_offset_in_bytes(), src_oop); decode_klass_not_null(klass); - } else { - z_lg(klass, oopDesc::klass_offset_in_bytes(), src_oop); } } void MacroAssembler::store_klass(Register klass, Register dst_oop, Register ck) { assert(!UseCompactObjectHeaders, "Don't use with compact headers"); - if (UseCompressedClassPointers) { - assert_different_registers(dst_oop, klass, Z_R0); - if (ck == noreg) ck = klass; - encode_klass_not_null(ck, klass); - z_st(ck, Address(dst_oop, oopDesc::klass_offset_in_bytes())); - } else { - z_stg(klass, Address(dst_oop, oopDesc::klass_offset_in_bytes())); - } + assert_different_registers(dst_oop, klass, Z_R0); + if (ck == noreg) ck = klass; + encode_klass_not_null(ck, klass); + z_st(ck, Address(dst_oop, oopDesc::klass_offset_in_bytes())); } void MacroAssembler::store_klass_gap(Register s, Register d) { assert(!UseCompactObjectHeaders, "Don't use with compact headers"); - if (UseCompressedClassPointers) { - assert(s != d, "not enough registers"); - // Support s = noreg. - if (s != noreg) { - z_st(s, Address(d, oopDesc::klass_gap_offset_in_bytes())); - } else { - z_mvhi(Address(d, oopDesc::klass_gap_offset_in_bytes()), 0); - } + assert(s != d, "not enough registers"); + // Support s = noreg. + if (s != noreg) { + z_st(s, Address(d, oopDesc::klass_gap_offset_in_bytes())); + } else { + z_mvhi(Address(d, oopDesc::klass_gap_offset_in_bytes()), 0); } } @@ -4227,67 +4198,64 @@ void MacroAssembler::compare_klass_ptr(Register Rop1, int64_t disp, Register Rba BLOCK_COMMENT("compare klass ptr {"); - if (UseCompressedClassPointers) { - const int shift = CompressedKlassPointers::shift(); - address base = CompressedKlassPointers::base(); + const int shift = CompressedKlassPointers::shift(); + address base = CompressedKlassPointers::base(); - if (UseCompactObjectHeaders) { - assert(shift >= 3, "cKlass encoder detected bad shift"); - } else { - assert((shift == 0) || (shift == 3), "cKlass encoder detected bad shift"); - } - assert_different_registers(Rop1, Z_R0); - assert_different_registers(Rop1, Rbase, Z_R1); - - // First encode register oop and then compare with cOop in memory. - // This sequence saves an unnecessary cOop load and decode. - if (base == nullptr) { - if (shift == 0) { - z_cl(Rop1, disp, Rbase); // Unscaled - } else { - z_srlg(Z_R0, Rop1, shift); // ZeroBased - z_cl(Z_R0, disp, Rbase); - } - } else { // HeapBased -#ifdef ASSERT - bool used_R0 = true; - bool used_R1 = true; -#endif - Register current = Rop1; - Label done; - - if (maybenull) { // null pointer must be preserved! - z_ltgr(Z_R0, current); - z_bre(done); - current = Z_R0; - } - - unsigned int base_h = ((unsigned long)base)>>32; - unsigned int base_l = (unsigned int)((unsigned long)base); - if ((base_h != 0) && (base_l == 0) && VM_Version::has_HighWordInstr()) { - lgr_if_needed(Z_R0, current); - z_aih(Z_R0, -((int)base_h)); // Base has no set bits in lower half. - } else if ((base_h == 0) && (base_l != 0)) { - lgr_if_needed(Z_R0, current); - z_agfi(Z_R0, -(int)base_l); - } else { - int pow2_offset = get_oop_base_complement(Z_R1, ((uint64_t)(intptr_t)base)); - add2reg_with_index(Z_R0, pow2_offset, Z_R1, Rop1); // Subtract base by adding complement. - } - - if (shift != 0) { - z_srlg(Z_R0, Z_R0, shift); - } - bind(done); - z_cl(Z_R0, disp, Rbase); -#ifdef ASSERT - if (used_R0) preset_reg(Z_R0, 0xb05bUL, 2); - if (used_R1) preset_reg(Z_R1, 0xb06bUL, 2); -#endif - } + if (UseCompactObjectHeaders) { + assert(shift >= 3, "cKlass encoder detected bad shift"); } else { - z_clg(Rop1, disp, Z_R0, Rbase); + assert((shift == 0) || (shift == 3), "cKlass encoder detected bad shift"); } + assert_different_registers(Rop1, Z_R0); + assert_different_registers(Rop1, Rbase, Z_R1); + + // First encode register oop and then compare with cOop in memory. + // This sequence saves an unnecessary cOop load and decode. + if (base == nullptr) { + if (shift == 0) { + z_cl(Rop1, disp, Rbase); // Unscaled + } else { + z_srlg(Z_R0, Rop1, shift); // ZeroBased + z_cl(Z_R0, disp, Rbase); + } + } else { // HeapBased +#ifdef ASSERT + bool used_R0 = true; + bool used_R1 = true; +#endif + Register current = Rop1; + Label done; + + if (maybenull) { // null pointer must be preserved! + z_ltgr(Z_R0, current); + z_bre(done); + current = Z_R0; + } + + unsigned int base_h = ((unsigned long)base)>>32; + unsigned int base_l = (unsigned int)((unsigned long)base); + if ((base_h != 0) && (base_l == 0) && VM_Version::has_HighWordInstr()) { + lgr_if_needed(Z_R0, current); + z_aih(Z_R0, -((int)base_h)); // Base has no set bits in lower half. + } else if ((base_h == 0) && (base_l != 0)) { + lgr_if_needed(Z_R0, current); + z_agfi(Z_R0, -(int)base_l); + } else { + int pow2_offset = get_oop_base_complement(Z_R1, ((uint64_t)(intptr_t)base)); + add2reg_with_index(Z_R0, pow2_offset, Z_R1, Rop1); // Subtract base by adding complement. + } + + if (shift != 0) { + z_srlg(Z_R0, Z_R0, shift); + } + bind(done); + z_cl(Z_R0, disp, Rbase); +#ifdef ASSERT + if (used_R0) preset_reg(Z_R0, 0xb05bUL, 2); + if (used_R1) preset_reg(Z_R1, 0xb06bUL, 2); +#endif + } + BLOCK_COMMENT("} compare klass ptr"); } diff --git a/src/hotspot/cpu/s390/macroAssembler_s390.hpp b/src/hotspot/cpu/s390/macroAssembler_s390.hpp index da24ae80d45..32e484d4790 100644 --- a/src/hotspot/cpu/s390/macroAssembler_s390.hpp +++ b/src/hotspot/cpu/s390/macroAssembler_s390.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2024 SAP SE. All rights reserved. * Copyright (c) 2024 IBM Corporation. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -842,8 +842,7 @@ class MacroAssembler: public Assembler { void store_klass(Register klass, Register dst_oop, Register ck = noreg); // Klass will get compressed if ck not provided. void store_klass_gap(Register s, Register dst_oop); void load_narrow_klass_compact(Register dst, Register src); - // Compares the Klass pointer of an object to a given Klass (which might be narrow, - // depending on UseCompressedClassPointers). + // Compares the narrow Klass pointer of an object to a given narrow Klass void cmp_klass(Register klass, Register obj, Register tmp); // Compares the Klass pointer of two objects obj1 and obj2. Result is in the condition flags. // Uses tmp1 and tmp2 as temporary registers. diff --git a/src/hotspot/cpu/s390/matcher_s390.hpp b/src/hotspot/cpu/s390/matcher_s390.hpp index 99461e33e3c..b04a6566d41 100644 --- a/src/hotspot/cpu/s390/matcher_s390.hpp +++ b/src/hotspot/cpu/s390/matcher_s390.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2017, 2024 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -82,7 +82,6 @@ static bool narrow_klass_use_complex_address() { NOT_LP64(ShouldNotCallThis()); - assert(UseCompressedClassPointers, "only for compressed klass code"); // TODO HS25: z port if (MatchDecodeNodes) return true; return false; } diff --git a/src/hotspot/cpu/s390/methodHandles_s390.cpp b/src/hotspot/cpu/s390/methodHandles_s390.cpp index e3de6d911be..dfb8ce09b27 100644 --- a/src/hotspot/cpu/s390/methodHandles_s390.cpp +++ b/src/hotspot/cpu/s390/methodHandles_s390.cpp @@ -120,16 +120,12 @@ void MethodHandles::verify_ref_kind(MacroAssembler* _masm, int ref_kind, __ z_nilf(temp, java_lang_invoke_MemberName::MN_REFERENCE_KIND_MASK); __ compare32_and_branch(temp, constant(ref_kind), Assembler::bcondEqual, L); - { - char *buf = NEW_C_HEAP_ARRAY(char, 100, mtInternal); - - jio_snprintf(buf, 100, "verify_ref_kind expected %x", ref_kind); - if (ref_kind == JVM_REF_invokeVirtual || ref_kind == JVM_REF_invokeSpecial) { - // Could do this for all ref_kinds, but would explode assembly code size. - trace_method_handle(_masm, buf); - } - __ stop(buf); + const char* msg = ref_kind_to_verify_msg(ref_kind); + if (ref_kind == JVM_REF_invokeVirtual || ref_kind == JVM_REF_invokeSpecial) { + // Could do this for all ref_kinds, but would explode assembly code size. + trace_method_handle(_masm, msg); } + __ stop(msg); BLOCK_COMMENT("} verify_ref_kind"); diff --git a/src/hotspot/cpu/s390/stubDeclarations_s390.hpp b/src/hotspot/cpu/s390/stubDeclarations_s390.hpp index c3ad3cefeb9..d0e26beedab 100644 --- a/src/hotspot/cpu/s390/stubDeclarations_s390.hpp +++ b/src/hotspot/cpu/s390/stubDeclarations_s390.hpp @@ -29,28 +29,32 @@ #define STUBGEN_PREUNIVERSE_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(preuniverse, 0) \ #define STUBGEN_INITIAL_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(initial, 20000) \ #define STUBGEN_CONTINUATION_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(continuation, 2000) \ #define STUBGEN_COMPILER_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(compiler, 20000 ) \ do_stub(compiler, partial_subtype_check) \ do_arch_entry(zarch, compiler, partial_subtype_check, \ @@ -60,7 +64,8 @@ #define STUBGEN_FINAL_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(final, 20000) \ diff --git a/src/hotspot/cpu/s390/stubGenerator_s390.cpp b/src/hotspot/cpu/s390/stubGenerator_s390.cpp index 2aa365be999..3f16312eb48 100644 --- a/src/hotspot/cpu/s390/stubGenerator_s390.cpp +++ b/src/hotspot/cpu/s390/stubGenerator_s390.cpp @@ -3422,7 +3422,7 @@ class StubGenerator: public StubCodeGenerator { } public: - StubGenerator(CodeBuffer* code, BlobId blob_id) : StubCodeGenerator(code, blob_id) { + StubGenerator(CodeBuffer* code, BlobId blob_id, AOTStubData* stub_data) : StubCodeGenerator(code, blob_id, stub_data) { switch(blob_id) { case BlobId::stubgen_preuniverse_id: generate_preuniverse_stubs(); @@ -3479,6 +3479,6 @@ class StubGenerator: public StubCodeGenerator { }; -void StubGenerator_generate(CodeBuffer* code, BlobId blob_id) { - StubGenerator g(code, blob_id); +void StubGenerator_generate(CodeBuffer* code, BlobId blob_id, AOTStubData* stub_data) { + StubGenerator g(code, blob_id, stub_data); } diff --git a/src/hotspot/cpu/s390/stubRoutines_s390.cpp b/src/hotspot/cpu/s390/stubRoutines_s390.cpp index 6feb20f9604..eda0ebfdecc 100644 --- a/src/hotspot/cpu/s390/stubRoutines_s390.cpp +++ b/src/hotspot/cpu/s390/stubRoutines_s390.cpp @@ -40,8 +40,12 @@ #define DEFINE_ARCH_ENTRY_INIT(arch, blob_name, stub_name, field_name, getter_name, init_function) \ address StubRoutines:: arch :: STUB_FIELD_NAME(field_name) = CAST_FROM_FN_PTR(address, init_function); -STUBGEN_ARCH_ENTRIES_DO(DEFINE_ARCH_ENTRY, DEFINE_ARCH_ENTRY_INIT) +#define DEFINE_ARCH_ENTRY_ARRAY(arch, blob_name, stub_name, field_name, getter_name, count) \ + address StubRoutines:: arch :: STUB_FIELD_NAME(field_name) [idx] ; +STUBGEN_ARCH_ENTRIES_DO(DEFINE_ARCH_ENTRY, DEFINE_ARCH_ENTRY_INIT, DEFINE_ARCH_ENTRY_ARRAY) + +#undef DEFINE_ARCH_ENTRY_ARRAY #undef DEFINE_ARCH_ENTRY_INIT #undef DEFINE_ARCH_ENTRY @@ -736,3 +740,9 @@ juint StubRoutines::zarch::_crc32c_table[CRC32_TABLES][CRC32_COLUMN_SIZE] = { } #endif }; + +#if INCLUDE_CDS +// nothing to do for s390 +void StubRoutines::init_AOTAddressTable() { +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/s390/stubRoutines_s390.hpp b/src/hotspot/cpu/s390/stubRoutines_s390.hpp index 0a07efae46c..e575115b731 100644 --- a/src/hotspot/cpu/s390/stubRoutines_s390.hpp +++ b/src/hotspot/cpu/s390/stubRoutines_s390.hpp @@ -81,9 +81,13 @@ class zarch { #define DECLARE_ARCH_ENTRY_INIT(arch, blob_name, stub_name, field_name, getter_name, init_function) \ DECLARE_ARCH_ENTRY(arch, blob_name, stub_name, field_name, getter_name) -private: - STUBGEN_ARCH_ENTRIES_DO(DECLARE_ARCH_ENTRY, DECLARE_ARCH_ENTRY_INIT) +#define DECLARE_ARCH_ENTRY_ARRAY(arch, blob_name, stub_name, field_name, getter_name, count) \ + static address STUB_FIELD_NAME(field_name) [count] ; +private: + STUBGEN_ARCH_ENTRIES_DO(DECLARE_ARCH_ENTRY, DECLARE_ARCH_ENTRY_INIT, DECLARE_ARCH_ENTRY_ARRAY) + +#undef DECLARE_ARCH_ENTRY_ARRAY #undef DECLARE_ARCH_ENTRY_INIT #undef DECLARE_ARCH_ENTRY @@ -108,8 +112,12 @@ private: #define DEFINE_ARCH_ENTRY_GETTER_INIT(arch, blob_name, stub_name, field_name, getter_name, init_function) \ DEFINE_ARCH_ENTRY_GETTER(arch, blob_name, stub_name, field_name, getter_name) - STUBGEN_ARCH_ENTRIES_DO(DEFINE_ARCH_ENTRY_GETTER, DEFINE_ARCH_ENTRY_GETTER_INIT) +#define DEFINE_ARCH_ENTRY_GETTER_ARRAY(arch, blob_name, stub_name, field_name, getter_name, count) \ + static address getter_name(int idx) { return STUB_FIELD_NAME(field_name) [idx] ; } + STUBGEN_ARCH_ENTRIES_DO(DEFINE_ARCH_ENTRY_GETTER, DEFINE_ARCH_ENTRY_GETTER_INIT, DEFINE_ARCH_ENTRY_GETTER_ARRAY) + +#undef DEFINE_ARCH_ENTRY_GETTER_ARRAY #undef DEFINE_ARCH_ENTRY_GETTER_INIT #undef DEFINE_ARCH_ENTRY_GETTER diff --git a/src/hotspot/cpu/s390/vm_version_s390.cpp b/src/hotspot/cpu/s390/vm_version_s390.cpp index 7f5b4870aab..7e9000991ca 100644 --- a/src/hotspot/cpu/s390/vm_version_s390.cpp +++ b/src/hotspot/cpu/s390/vm_version_s390.cpp @@ -289,10 +289,6 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UseSHA3Intrinsics, false); } - if (!(UseSHA1Intrinsics || UseSHA256Intrinsics || UseSHA512Intrinsics)) { - FLAG_SET_DEFAULT(UseSHA, false); - } - if (UseSecondarySupersTable && VM_Version::get_model_index() < 5 /* z196/z11 */) { if (!FLAG_IS_DEFAULT(UseSecondarySupersTable)) { warning("UseSecondarySupersTable requires z196 or later."); diff --git a/src/hotspot/cpu/x86/assembler_x86.cpp b/src/hotspot/cpu/x86/assembler_x86.cpp index 38a28a6ec49..a4f2968f0d1 100644 --- a/src/hotspot/cpu/x86/assembler_x86.cpp +++ b/src/hotspot/cpu/x86/assembler_x86.cpp @@ -3472,7 +3472,7 @@ void Assembler::vmovdqu(XMMRegister dst, XMMRegister src) { emit_int16(0x6F, (0xC0 | encode)); } -void Assembler::vmovw(XMMRegister dst, Register src) { +void Assembler::evmovw(XMMRegister dst, Register src) { assert(VM_Version::supports_avx512_fp16(), "requires AVX512-FP16"); InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_is_evex_instruction(); @@ -3480,7 +3480,7 @@ void Assembler::vmovw(XMMRegister dst, Register src) { emit_int16(0x6E, (0xC0 | encode)); } -void Assembler::vmovw(Register dst, XMMRegister src) { +void Assembler::evmovw(Register dst, XMMRegister src) { assert(VM_Version::supports_avx512_fp16(), "requires AVX512-FP16"); InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_is_evex_instruction(); @@ -3488,6 +3488,36 @@ void Assembler::vmovw(Register dst, XMMRegister src) { emit_int16(0x7E, (0xC0 | encode)); } +void Assembler::evmovw(XMMRegister dst, Address src) { + assert(VM_Version::supports_avx10_2(), ""); + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_16bit); + attributes.set_is_evex_instruction(); + vex_prefix(src, 0, dst->encoding(), VEX_SIMD_F3, VEX_OPCODE_MAP5, &attributes); + emit_int8(0x6E); + emit_operand(dst, src, 0); +} + +void Assembler::evmovw(Address dst, XMMRegister src) { + assert(VM_Version::supports_avx10_2(), ""); + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_16bit); + attributes.set_is_evex_instruction(); + vex_prefix(dst, 0, src->encoding(), VEX_SIMD_F3, VEX_OPCODE_MAP5, &attributes); + emit_int8(0x7E); + emit_operand(src, dst, 0); +} + +void Assembler::evmovw(XMMRegister dst, XMMRegister src) { + assert(VM_Version::supports_avx10_2(), ""); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_is_evex_instruction(); + int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_F3, VEX_OPCODE_MAP5, &attributes); + emit_int16(0x6E, (0xC0 | encode)); +} + void Assembler::vmovdqu(XMMRegister dst, Address src) { assert(UseAVX > 0, ""); InstructionMark im(this); @@ -7310,6 +7340,42 @@ void Assembler::etzcntq(Register dst, Address src, bool no_flags) { emit_operand(dst, src, 0); } +void Assembler::evucomish(XMMRegister dst, Address src) { + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_16bit); + attributes.set_is_evex_instruction(); + vex_prefix(src, 0, dst->encoding(), VEX_SIMD_NONE, VEX_OPCODE_MAP5, &attributes); + emit_int8(0x2E); + emit_operand(dst, src, 0); +} + +void Assembler::evucomish(XMMRegister dst, XMMRegister src) { + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_is_evex_instruction(); + int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_MAP5, &attributes); + emit_int16(0x2E, (0xC0 | encode)); +} + +void Assembler::evucomxsh(XMMRegister dst, Address src) { + assert(VM_Version::supports_avx10_2(), ""); + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_16bit); + attributes.set_is_evex_instruction(); + vex_prefix(src, 0, dst->encoding(), VEX_SIMD_F3, VEX_OPCODE_MAP5, &attributes); + emit_int8(0x2E); + emit_operand(dst, src, 0); +} + +void Assembler::evucomxsh(XMMRegister dst, XMMRegister src) { + assert(VM_Version::supports_avx10_2(), ""); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_is_evex_instruction(); + int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_F3, VEX_OPCODE_MAP5, &attributes); + emit_int16(0x2E, (0xC0 | encode)); +} + void Assembler::ucomisd(XMMRegister dst, Address src) { InstructionMark im(this); InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); @@ -7327,7 +7393,7 @@ void Assembler::ucomisd(XMMRegister dst, XMMRegister src) { emit_int16(0x2E, (0xC0 | encode)); } -void Assembler::vucomxsd(XMMRegister dst, Address src) { +void Assembler::evucomxsd(XMMRegister dst, Address src) { assert(VM_Version::supports_avx10_2(), ""); InstructionMark im(this); InstructionAttr attributes(AVX_128bit, /* rex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); @@ -7338,7 +7404,7 @@ void Assembler::vucomxsd(XMMRegister dst, Address src) { emit_operand(dst, src, 0); } -void Assembler::vucomxsd(XMMRegister dst, XMMRegister src) { +void Assembler::evucomxsd(XMMRegister dst, XMMRegister src) { assert(VM_Version::supports_avx10_2(), ""); InstructionAttr attributes(AVX_128bit, /* rex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_is_evex_instruction(); @@ -7361,7 +7427,7 @@ void Assembler::ucomiss(XMMRegister dst, XMMRegister src) { emit_int16(0x2E, (0xC0 | encode)); } -void Assembler::vucomxss(XMMRegister dst, Address src) { +void Assembler::evucomxss(XMMRegister dst, Address src) { assert(VM_Version::supports_avx10_2(), ""); InstructionMark im(this); InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); @@ -7372,7 +7438,7 @@ void Assembler::vucomxss(XMMRegister dst, Address src) { emit_operand(dst, src, 0); } -void Assembler::vucomxss(XMMRegister dst, XMMRegister src) { +void Assembler::evucomxss(XMMRegister dst, XMMRegister src) { assert(VM_Version::supports_avx10_2(), ""); InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_is_evex_instruction(); @@ -8411,30 +8477,6 @@ void Assembler::vmulsh(XMMRegister dst, XMMRegister nds, XMMRegister src) { emit_int16(0x59, (0xC0 | encode)); } -void Assembler::vmaxsh(XMMRegister dst, XMMRegister nds, XMMRegister src) { - assert(VM_Version::supports_avx512_fp16(), "requires AVX512-FP16"); - InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); - attributes.set_is_evex_instruction(); - int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_F3, VEX_OPCODE_MAP5, &attributes); - emit_int16(0x5F, (0xC0 | encode)); -} - -void Assembler::eminmaxsh(XMMRegister dst, XMMRegister nds, XMMRegister src, int imm8) { - assert(VM_Version::supports_avx10_2(), ""); - InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); - attributes.set_is_evex_instruction(); - int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_3A, &attributes); - emit_int24(0x53, (0xC0 | encode), imm8); -} - -void Assembler::vminsh(XMMRegister dst, XMMRegister nds, XMMRegister src) { - assert(VM_Version::supports_avx512_fp16(), "requires AVX512-FP16"); - InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); - attributes.set_is_evex_instruction(); - int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_F3, VEX_OPCODE_MAP5, &attributes); - emit_int16(0x5D, (0xC0 | encode)); -} - void Assembler::vsqrtsh(XMMRegister dst, XMMRegister src) { assert(VM_Version::supports_avx512_fp16(), "requires AVX512-FP16"); InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); @@ -13369,48 +13411,38 @@ bool Assembler::is_demotable(bool no_flags, int dst_enc, int nds_enc) { return (!no_flags && dst_enc == nds_enc); } -void Assembler::vmaxss(XMMRegister dst, XMMRegister nds, XMMRegister src) { - assert(VM_Version::supports_avx(), ""); - InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); - int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); - emit_int16(0x5F, (0xC0 | encode)); -} - -void Assembler::vmaxsd(XMMRegister dst, XMMRegister nds, XMMRegister src) { - assert(VM_Version::supports_avx(), ""); - InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); - attributes.set_rex_vex_w_reverted(); - int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); - emit_int16(0x5F, (0xC0 | encode)); -} - -void Assembler::vminss(XMMRegister dst, XMMRegister nds, XMMRegister src) { - assert(VM_Version::supports_avx(), ""); - InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); - int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); - emit_int16(0x5D, (0xC0 | encode)); -} - -void Assembler::eminmaxss(XMMRegister dst, XMMRegister nds, XMMRegister src, int imm8) { +void Assembler::evminmaxsh(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int imm8) { assert(VM_Version::supports_avx10_2(), ""); - InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); attributes.set_is_evex_instruction(); + attributes.set_embedded_opmask_register_specifier(mask); + if (merge) { + attributes.reset_is_clear_context(); + } + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_3A, &attributes); + emit_int24(0x53, (0xC0 | encode), imm8); +} + +void Assembler::evminmaxss(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int imm8) { + assert(VM_Version::supports_avx10_2(), ""); + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + attributes.set_is_evex_instruction(); + attributes.set_embedded_opmask_register_specifier(mask); + if (merge) { + attributes.reset_is_clear_context(); + } int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int24(0x53, (0xC0 | encode), imm8); } -void Assembler::vminsd(XMMRegister dst, XMMRegister nds, XMMRegister src) { - assert(VM_Version::supports_avx(), ""); - InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); - attributes.set_rex_vex_w_reverted(); - int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); - emit_int16(0x5D, (0xC0 | encode)); -} - -void Assembler::eminmaxsd(XMMRegister dst, XMMRegister nds, XMMRegister src, int imm8) { +void Assembler::evminmaxsd(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int imm8) { assert(VM_Version::supports_avx10_2(), ""); - InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); attributes.set_is_evex_instruction(); + attributes.set_embedded_opmask_register_specifier(mask); + if (merge) { + attributes.reset_is_clear_context(); + } int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int24(0x53, (0xC0 | encode), imm8); } diff --git a/src/hotspot/cpu/x86/assembler_x86.hpp b/src/hotspot/cpu/x86/assembler_x86.hpp index 57a5e25d7a6..98684752b0c 100644 --- a/src/hotspot/cpu/x86/assembler_x86.hpp +++ b/src/hotspot/cpu/x86/assembler_x86.hpp @@ -1694,8 +1694,11 @@ private: void movsbl(Register dst, Address src); void movsbl(Register dst, Register src); - void vmovw(XMMRegister dst, Register src); - void vmovw(Register dst, XMMRegister src); + void evmovw(XMMRegister dst, Register src); + void evmovw(Register dst, XMMRegister src); + void evmovw(XMMRegister dst, Address src); + void evmovw(Address dst, XMMRegister src); + void evmovw(XMMRegister dst, XMMRegister src); void movsbq(Register dst, Address src); void movsbq(Register dst, Register src); @@ -2329,17 +2332,23 @@ private: void tzcntq(Register dst, Address src); void etzcntq(Register dst, Address src, bool no_flags); + // Unordered Compare Scalar Half-Precision Floating-Point Values and set EFLAGS + void evucomish(XMMRegister dst, Address src); + void evucomish(XMMRegister dst, XMMRegister src); + void evucomxsh(XMMRegister dst, Address src); + void evucomxsh(XMMRegister dst, XMMRegister src); + // Unordered Compare Scalar Double-Precision Floating-Point Values and set EFLAGS void ucomisd(XMMRegister dst, Address src); void ucomisd(XMMRegister dst, XMMRegister src); - void vucomxsd(XMMRegister dst, Address src); - void vucomxsd(XMMRegister dst, XMMRegister src); + void evucomxsd(XMMRegister dst, Address src); + void evucomxsd(XMMRegister dst, XMMRegister src); // Unordered Compare Scalar Single-Precision Floating-Point Values and set EFLAGS void ucomiss(XMMRegister dst, Address src); void ucomiss(XMMRegister dst, XMMRegister src); - void vucomxss(XMMRegister dst, Address src); - void vucomxss(XMMRegister dst, XMMRegister src); + void evucomxss(XMMRegister dst, Address src); + void evucomxss(XMMRegister dst, XMMRegister src); void xabort(int8_t imm8); @@ -2417,11 +2426,6 @@ private: void vsubss(XMMRegister dst, XMMRegister nds, Address src); void vsubss(XMMRegister dst, XMMRegister nds, XMMRegister src); - void vmaxss(XMMRegister dst, XMMRegister nds, XMMRegister src); - void vmaxsd(XMMRegister dst, XMMRegister nds, XMMRegister src); - void vminss(XMMRegister dst, XMMRegister nds, XMMRegister src); - void vminsd(XMMRegister dst, XMMRegister nds, XMMRegister src); - void sarxl(Register dst, Register src1, Register src2); void sarxl(Register dst, Address src1, Register src2); void sarxq(Register dst, Register src1, Register src2); @@ -2552,8 +2556,6 @@ private: void vsubsh(XMMRegister dst, XMMRegister nds, XMMRegister src); void vmulsh(XMMRegister dst, XMMRegister nds, XMMRegister src); void vdivsh(XMMRegister dst, XMMRegister nds, XMMRegister src); - void vmaxsh(XMMRegister dst, XMMRegister nds, XMMRegister src); - void vminsh(XMMRegister dst, XMMRegister nds, XMMRegister src); void vsqrtsh(XMMRegister dst, XMMRegister src); void vfmadd132sh(XMMRegister dst, XMMRegister src1, XMMRegister src2); @@ -2790,9 +2792,9 @@ private: void vminpd(XMMRegister dst, XMMRegister src1, XMMRegister src2, int vector_len); // AVX10.2 floating point minmax instructions - void eminmaxsh(XMMRegister dst, XMMRegister nds, XMMRegister src, int imm8); - void eminmaxss(XMMRegister dst, XMMRegister nds, XMMRegister src, int imm8); - void eminmaxsd(XMMRegister dst, XMMRegister nds, XMMRegister src, int imm8); + void evminmaxsh(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int imm8); + void evminmaxss(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int imm8); + void evminmaxsd(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int imm8); void evminmaxph(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int imm8, int vector_len); void evminmaxph(XMMRegister dst, KRegister mask, XMMRegister nds, Address src, bool merge, int imm8, int vector_len); void evminmaxps(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int imm8, int vector_len); diff --git a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp index d9be0fdcc8d..5c05b3702bb 100644 --- a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -42,6 +42,7 @@ #include "runtime/safepointMechanism.hpp" #include "runtime/sharedRuntime.hpp" #include "runtime/stubRoutines.hpp" +#include "runtime/threadIdentifier.hpp" #include "utilities/powerOfTwo.hpp" #include "vmreg_x86.inline.hpp" @@ -70,6 +71,17 @@ static jlong *double_signmask_pool = double_quadword(&fp_signmask_pool[2*2], static jlong *float_signflip_pool = double_quadword(&fp_signmask_pool[3*2], (jlong)UCONST64(0x8000000080000000), (jlong)UCONST64(0x8000000080000000)); static jlong *double_signflip_pool = double_quadword(&fp_signmask_pool[4*2], (jlong)UCONST64(0x8000000000000000), (jlong)UCONST64(0x8000000000000000)); +#if INCLUDE_CDS +// publish external addresses defined in this file +void LIR_Assembler::init_AOTAddressTable(GrowableArray
& external_addresses) { +#define ADD(addr) external_addresses.append((address)(addr)); + ADD(float_signmask_pool); + ADD(double_signmask_pool); + ADD(float_signflip_pool); + ADD(double_signflip_pool); +#undef ADD +} +#endif // INCLUDE_CDS NEEDS_CLEANUP // remove this definitions ? const Register SYNC_header = rax; // synchronization header @@ -77,23 +89,6 @@ const Register SHIFT_count = rcx; // where count for shift operations must be #define __ _masm-> - -static void select_different_registers(Register preserve, - Register extra, - Register &tmp1, - Register &tmp2) { - if (tmp1 == preserve) { - assert_different_registers(tmp1, tmp2, extra); - tmp1 = extra; - } else if (tmp2 == preserve) { - assert_different_registers(tmp1, tmp2, extra); - tmp2 = extra; - } - assert_different_registers(preserve, tmp1, tmp2); -} - - - static void select_different_registers(Register preserve, Register extra, Register &tmp1, @@ -535,10 +530,23 @@ void LIR_Assembler::const2reg(LIR_Opr src, LIR_Opr dest, LIR_PatchCode patch_cod } case T_LONG: { +#if INCLUDE_CDS + if (AOTCodeCache::is_on_for_dump()) { + address b = c->as_pointer(); + if (b == (address)ThreadIdentifier::unsafe_offset()) { + __ lea(dest->as_register_lo(), ExternalAddress(b)); + break; + } + } +#endif assert(patch_code == lir_patch_none, "no patching handled here"); #if INCLUDE_CDS if (AOTCodeCache::is_on_for_dump()) { address b = c->as_pointer(); + if (b == (address)ThreadIdentifier::unsafe_offset()) { + __ lea(dest->as_register_lo(), ExternalAddress(b)); + break; + } if (AOTRuntimeConstants::contains(b)) { __ load_aotrc_address(dest->as_register_lo(), b); break; @@ -1309,12 +1317,8 @@ void LIR_Assembler::emit_typecheck_helper(LIR_OpTypeCheck *op, Label* success, L } else if (obj == klass_RInfo) { klass_RInfo = dst; } - if (k->is_loaded() && !UseCompressedClassPointers) { - select_different_registers(obj, dst, k_RInfo, klass_RInfo); - } else { - Rtmp1 = op->tmp3()->as_register(); - select_different_registers(obj, dst, k_RInfo, klass_RInfo, Rtmp1); - } + Rtmp1 = op->tmp3()->as_register(); + select_different_registers(obj, dst, k_RInfo, klass_RInfo, Rtmp1); assert_different_registers(obj, k_RInfo, klass_RInfo); @@ -1348,12 +1352,8 @@ void LIR_Assembler::emit_typecheck_helper(LIR_OpTypeCheck *op, Label* success, L if (op->fast_check()) { // get object class // not a safepoint as obj null check happens earlier - if (UseCompressedClassPointers) { - __ load_klass(Rtmp1, obj, tmp_load_klass); - __ cmpptr(k_RInfo, Rtmp1); - } else { - __ cmpptr(k_RInfo, Address(obj, oopDesc::klass_offset_in_bytes())); - } + __ load_klass(Rtmp1, obj, tmp_load_klass); + __ cmpptr(k_RInfo, Rtmp1); __ jcc(Assembler::notEqual, *failure_target); // successful cast, fall through to profile or jump } else { @@ -2651,9 +2651,7 @@ void LIR_Assembler::emit_arraycopy(LIR_OpArrayCopy* op) { // but not necessarily exactly of type default_type. Label known_ok, halt; __ mov_metadata(tmp, default_type->constant_encoding()); - if (UseCompressedClassPointers) { - __ encode_klass_not_null(tmp, rscratch1); - } + __ encode_klass_not_null(tmp, rscratch1); if (basic_type != T_OBJECT) { __ cmp_klass(tmp, dst, tmp2); diff --git a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.hpp b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.hpp index c4a368b54d8..6f179255e4a 100644 --- a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.hpp @@ -58,4 +58,7 @@ public: void store_parameter(jobject c, int offset_from_esp_in_words); void store_parameter(Metadata* c, int offset_from_esp_in_words); +#if INCLUDE_CDS + void static init_AOTAddressTable(GrowableArray
& external_addresses); +#endif // INCLUDE_CDS #endif // CPU_X86_C1_LIRASSEMBLER_X86_HPP diff --git a/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp b/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp index 5459e8df229..cc068cda7a9 100644 --- a/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp +++ b/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, 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 @@ -1291,9 +1291,7 @@ void LIRGenerator::do_CheckCast(CheckCast* x) { } LIR_Opr reg = rlock_result(x); LIR_Opr tmp3 = LIR_OprFact::illegalOpr; - if (!x->klass()->is_loaded() || UseCompressedClassPointers) { - tmp3 = new_register(objectType); - } + tmp3 = new_register(objectType); __ checkcast(reg, obj.result(), x->klass(), new_register(objectType), new_register(objectType), tmp3, x->direct_compare(), info_for_exception, patching_info, stub, @@ -1313,9 +1311,7 @@ void LIRGenerator::do_InstanceOf(InstanceOf* x) { } obj.load_item(); LIR_Opr tmp3 = LIR_OprFact::illegalOpr; - if (!x->klass()->is_loaded() || UseCompressedClassPointers) { - tmp3 = new_register(objectType); - } + tmp3 = new_register(objectType); __ instanceof(reg, obj.result(), x->klass(), new_register(objectType), new_register(objectType), tmp3, x->direct_compare(), patching_info, x->profiled_method(), x->profiled_bci()); @@ -1436,4 +1432,5 @@ void LIRGenerator::volatile_field_load(LIR_Address* address, LIR_Opr result, } else { __ load(address, result, info); } + __ membar_acquire(); } diff --git a/src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp b/src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp index 88e2e6c8ba9..7adaea48ff1 100644 --- a/src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, 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,14 +85,11 @@ void C1_MacroAssembler::initialize_header(Register obj, Register klass, Register if (UseCompactObjectHeaders) { movptr(t1, Address(klass, Klass::prototype_header_offset())); movptr(Address(obj, oopDesc::mark_offset_in_bytes()), t1); - } else if (UseCompressedClassPointers) { // Take care not to kill klass + } else { // Take care not to kill klass movptr(Address(obj, oopDesc::mark_offset_in_bytes()), checked_cast(markWord::prototype().value())); movptr(t1, klass); encode_klass_not_null(t1, rscratch1); movl(Address(obj, oopDesc::klass_offset_in_bytes()), t1); - } else { - movptr(Address(obj, oopDesc::mark_offset_in_bytes()), checked_cast(markWord::prototype().value())); - movptr(Address(obj, oopDesc::klass_offset_in_bytes()), klass); } if (len->is_valid()) { @@ -104,7 +101,7 @@ void C1_MacroAssembler::initialize_header(Register obj, Register klass, Register xorl(t1, t1); movl(Address(obj, base_offset), t1); } - } else if (UseCompressedClassPointers && !UseCompactObjectHeaders) { + } else if (!UseCompactObjectHeaders) { xorptr(t1, t1); store_klass_gap(obj, t1); } diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp index 5b5fb02967c..b4d8aa10de2 100644 --- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp @@ -152,7 +152,7 @@ inline Assembler::AvxVectorLen C2_MacroAssembler::vector_length_encoding(int vle // Because the transitions from emitted code to the runtime // monitorenter/exit helper stubs are so slow it's critical that -// we inline both the stack-locking fast path and the inflated fast path. +// we inline both the lock-stack fast path and the inflated fast path. // // See also: cmpFastLock and cmpFastUnlock. // @@ -1037,8 +1037,8 @@ void C2_MacroAssembler::evminmax_fp(int opcode, BasicType elem_bt, } } -void C2_MacroAssembler::vminmax_fp(int opc, BasicType elem_bt, XMMRegister dst, KRegister mask, - XMMRegister src1, XMMRegister src2, int vlen_enc) { +void C2_MacroAssembler::vminmax_fp_avx10_2(int opc, BasicType elem_bt, XMMRegister dst, KRegister mask, + XMMRegister src1, XMMRegister src2, int vlen_enc) { assert(opc == Op_MinV || opc == Op_MinReductionV || opc == Op_MaxV || opc == Op_MaxReductionV, "sanity"); @@ -1052,6 +1052,21 @@ void C2_MacroAssembler::vminmax_fp(int opc, BasicType elem_bt, XMMRegister dst, } } +void C2_MacroAssembler::sminmax_fp_avx10_2(int opc, BasicType elem_bt, XMMRegister dst, KRegister mask, + XMMRegister src1, XMMRegister src2) { + assert(opc == Op_MinF || opc == Op_MaxF || + opc == Op_MinD || opc == Op_MaxD, "sanity"); + + int imm8 = (opc == Op_MinF || opc == Op_MinD) ? AVX10_2_MINMAX_MIN_COMPARE_SIGN + : AVX10_2_MINMAX_MAX_COMPARE_SIGN; + if (elem_bt == T_FLOAT) { + evminmaxss(dst, mask, src1, src2, true, imm8); + } else { + assert(elem_bt == T_DOUBLE, ""); + evminmaxsd(dst, mask, src1, src2, true, imm8); + } +} + // Float/Double signum void C2_MacroAssembler::signum_fp(int opcode, XMMRegister dst, XMMRegister zero, XMMRegister one) { assert(opcode == Op_SignumF || opcode == Op_SignumD, "sanity"); @@ -1063,7 +1078,7 @@ void C2_MacroAssembler::signum_fp(int opcode, XMMRegister dst, XMMRegister zero, // If other floating point comparison instructions used, ZF=1 for equal and unordered cases if (opcode == Op_SignumF) { if (VM_Version::supports_avx10_2()) { - vucomxss(dst, zero); + evucomxss(dst, zero); jcc(Assembler::negative, DONE_LABEL); } else { ucomiss(dst, zero); @@ -1074,7 +1089,7 @@ void C2_MacroAssembler::signum_fp(int opcode, XMMRegister dst, XMMRegister zero, xorps(dst, ExternalAddress(StubRoutines::x86::vector_float_sign_flip()), noreg); } else if (opcode == Op_SignumD) { if (VM_Version::supports_avx10_2()) { - vucomxsd(dst, zero); + evucomxsd(dst, zero); jcc(Assembler::negative, DONE_LABEL); } else { ucomisd(dst, zero); @@ -1691,12 +1706,8 @@ void C2_MacroAssembler::load_constant_vector(BasicType bt, XMMRegister dst, Inte } void C2_MacroAssembler::load_iota_indices(XMMRegister dst, int vlen_in_bytes, BasicType bt) { - // The iota indices are ordered by type B/S/I/L/F/D, and the offset between two types is 64. - int offset = exact_log2(type2aelembytes(bt)) << 6; - if (is_floating_point_type(bt)) { - offset += 128; - } - ExternalAddress addr(StubRoutines::x86::vector_iota_indices() + offset); + int entry_idx = vector_iota_entry_index(bt); + ExternalAddress addr(StubRoutines::x86::vector_iota_indices(entry_idx)); load_vector(T_BYTE, dst, addr, vlen_in_bytes); } @@ -2135,8 +2146,8 @@ void C2_MacroAssembler::mulreduce16B(int opcode, Register dst, Register src1, XM } else { pmovsxbw(vtmp2, src2); reduce8S(opcode, dst, src1, vtmp2, vtmp1, vtmp2); - pshufd(vtmp2, src2, 0x1); - pmovsxbw(vtmp2, src2); + pshufd(vtmp2, src2, 0xe); + pmovsxbw(vtmp2, vtmp2); reduce8S(opcode, dst, dst, vtmp2, vtmp1, vtmp2); } } @@ -2145,7 +2156,7 @@ void C2_MacroAssembler::mulreduce32B(int opcode, Register dst, Register src1, XM if (UseAVX > 2 && VM_Version::supports_avx512bw()) { int vector_len = Assembler::AVX_512bit; vpmovsxbw(vtmp1, src2, vector_len); - reduce32S(opcode, dst, src1, vtmp1, vtmp1, vtmp2); + reduce32S(opcode, dst, src1, vtmp1, vtmp2, vtmp1); } else { assert(UseAVX >= 2,"Should not reach here."); mulreduce16B(opcode, dst, src1, src2, vtmp1, vtmp2); @@ -2192,6 +2203,7 @@ void C2_MacroAssembler::reduce8S(int opcode, Register dst, Register src1, XMMReg } phaddw(vtmp1, src2); } else { + assert_different_registers(src2, vtmp1); pshufd(vtmp1, src2, 0xE); reduce_operation_128(T_SHORT, opcode, vtmp1, src2); } @@ -2204,6 +2216,7 @@ void C2_MacroAssembler::reduce16S(int opcode, Register dst, Register src1, XMMRe vphaddw(vtmp2, src2, src2, vector_len); vpermq(vtmp2, vtmp2, 0xD8, vector_len); } else { + assert_different_registers(src2, vtmp2); vextracti128_high(vtmp2, src2); reduce_operation_128(T_SHORT, opcode, vtmp2, src2); } @@ -2211,6 +2224,7 @@ void C2_MacroAssembler::reduce16S(int opcode, Register dst, Register src1, XMMRe } void C2_MacroAssembler::reduce32S(int opcode, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2) { + assert_different_registers(src2, vtmp1); int vector_len = Assembler::AVX_256bit; vextracti64x4_high(vtmp1, src2); reduce_operation_256(T_SHORT, opcode, vtmp1, vtmp1, src2); @@ -2400,7 +2414,7 @@ void C2_MacroAssembler::reduceFloatMinMax(int opcode, int vlen, bool is_dst_vali } if (VM_Version::supports_avx10_2()) { - vminmax_fp(opcode, T_FLOAT, wdst, k0, wtmp, wsrc, vlen_enc); + vminmax_fp_avx10_2(opcode, T_FLOAT, wdst, k0, wtmp, wsrc, vlen_enc); } else { vminmax_fp(opcode, T_FLOAT, wdst, wtmp, wsrc, tmp, atmp, btmp, vlen_enc); } @@ -2409,7 +2423,7 @@ void C2_MacroAssembler::reduceFloatMinMax(int opcode, int vlen, bool is_dst_vali } if (is_dst_valid) { if (VM_Version::supports_avx10_2()) { - vminmax_fp(opcode, T_FLOAT, dst, k0, wdst, dst, Assembler::AVX_128bit); + vminmax_fp_avx10_2(opcode, T_FLOAT, dst, k0, wdst, dst, Assembler::AVX_128bit); } else { vminmax_fp(opcode, T_FLOAT, dst, wdst, dst, tmp, atmp, btmp, Assembler::AVX_128bit); } @@ -2440,7 +2454,7 @@ void C2_MacroAssembler::reduceDoubleMinMax(int opcode, int vlen, bool is_dst_val } if (VM_Version::supports_avx10_2()) { - vminmax_fp(opcode, T_DOUBLE, wdst, k0, wtmp, wsrc, vlen_enc); + vminmax_fp_avx10_2(opcode, T_DOUBLE, wdst, k0, wtmp, wsrc, vlen_enc); } else { vminmax_fp(opcode, T_DOUBLE, wdst, wtmp, wsrc, tmp, atmp, btmp, vlen_enc); } @@ -2451,7 +2465,7 @@ void C2_MacroAssembler::reduceDoubleMinMax(int opcode, int vlen, bool is_dst_val if (is_dst_valid) { if (VM_Version::supports_avx10_2()) { - vminmax_fp(opcode, T_DOUBLE, dst, k0, wdst, dst, Assembler::AVX_128bit); + vminmax_fp_avx10_2(opcode, T_DOUBLE, dst, k0, wdst, dst, Assembler::AVX_128bit); } else { vminmax_fp(opcode, T_DOUBLE, dst, wdst, dst, tmp, atmp, btmp, Assembler::AVX_128bit); } @@ -7061,13 +7075,25 @@ void C2_MacroAssembler::evfp16ph(int opcode, XMMRegister dst, XMMRegister src1, } } -void C2_MacroAssembler::scalar_max_min_fp16(int opcode, XMMRegister dst, XMMRegister src1, XMMRegister src2, - KRegister ktmp, XMMRegister xtmp1, XMMRegister xtmp2) { - vector_max_min_fp16(opcode, dst, src1, src2, ktmp, xtmp1, xtmp2, Assembler::AVX_128bit); +void C2_MacroAssembler::sminmax_fp16(int opcode, XMMRegister dst, XMMRegister src1, XMMRegister src2, + KRegister ktmp, XMMRegister xtmp1, XMMRegister xtmp2) { + vminmax_fp16(opcode, dst, src1, src2, ktmp, xtmp1, xtmp2, Assembler::AVX_128bit); } -void C2_MacroAssembler::vector_max_min_fp16(int opcode, XMMRegister dst, XMMRegister src1, XMMRegister src2, - KRegister ktmp, XMMRegister xtmp1, XMMRegister xtmp2, int vlen_enc) { +void C2_MacroAssembler::sminmax_fp16_avx10_2(int opcode, XMMRegister dst, XMMRegister src1, XMMRegister src2, + KRegister ktmp) { + if (opcode == Op_MaxHF) { + // dst = max(src1, src2) + evminmaxsh(dst, ktmp, src1, src2, true, AVX10_2_MINMAX_MAX_COMPARE_SIGN); + } else { + assert(opcode == Op_MinHF, ""); + // dst = min(src1, src2) + evminmaxsh(dst, ktmp, src1, src2, true, AVX10_2_MINMAX_MIN_COMPARE_SIGN); + } +} + +void C2_MacroAssembler::vminmax_fp16(int opcode, XMMRegister dst, XMMRegister src1, XMMRegister src2, + KRegister ktmp, XMMRegister xtmp1, XMMRegister xtmp2, int vlen_enc) { if (opcode == Op_MaxVHF || opcode == Op_MaxHF) { // Move sign bits of src2 to mask register. evpmovw2m(ktmp, src2, vlen_enc); @@ -7110,3 +7136,48 @@ void C2_MacroAssembler::vector_max_min_fp16(int opcode, XMMRegister dst, XMMRegi Assembler::evmovdquw(dst, ktmp, xtmp1, true, vlen_enc); } } + +void C2_MacroAssembler::vminmax_fp16_avx10_2(int opcode, XMMRegister dst, XMMRegister src1, XMMRegister src2, + KRegister ktmp, int vlen_enc) { + if (opcode == Op_MaxVHF) { + // dst = max(src1, src2) + evminmaxph(dst, ktmp, src1, src2, true, AVX10_2_MINMAX_MAX_COMPARE_SIGN, vlen_enc); + } else { + assert(opcode == Op_MinVHF, ""); + // dst = min(src1, src2) + evminmaxph(dst, ktmp, src1, src2, true, AVX10_2_MINMAX_MIN_COMPARE_SIGN, vlen_enc); + } +} + +void C2_MacroAssembler::vminmax_fp16_avx10_2(int opcode, XMMRegister dst, XMMRegister src1, Address src2, + KRegister ktmp, int vlen_enc) { + if (opcode == Op_MaxVHF) { + // dst = max(src1, src2) + evminmaxph(dst, ktmp, src1, src2, true, AVX10_2_MINMAX_MAX_COMPARE_SIGN, vlen_enc); + } else { + assert(opcode == Op_MinVHF, ""); + // dst = min(src1, src2) + evminmaxph(dst, ktmp, src1, src2, true, AVX10_2_MINMAX_MIN_COMPARE_SIGN, vlen_enc); + } +} + +int C2_MacroAssembler::vector_iota_entry_index(BasicType bt) { + // The vector iota entries array is ordered by type B/S/I/L/F/D, and + // the offset between two types is 16. + switch(bt) { + case T_BYTE: + return 0; + case T_SHORT: + return 1; + case T_INT: + return 2; + case T_LONG: + return 3; + case T_FLOAT: + return 4; + case T_DOUBLE: + return 5; + default: + ShouldNotReachHere(); + } +} diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp index 6d8b0ceaebe..9b229ad7221 100644 --- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -67,8 +67,11 @@ public: XMMRegister tmp, XMMRegister atmp, XMMRegister btmp, int vlen_enc); - void vminmax_fp(int opc, BasicType elem_bt, XMMRegister dst, KRegister mask, - XMMRegister src1, XMMRegister src2, int vlen_enc); + void vminmax_fp_avx10_2(int opc, BasicType elem_bt, XMMRegister dst, KRegister mask, + XMMRegister src1, XMMRegister src2, int vlen_enc); + + void sminmax_fp_avx10_2(int opc, BasicType elem_bt, XMMRegister dst, KRegister mask, + XMMRegister src1, XMMRegister src2); void vpuminmaxq(int opcode, XMMRegister dst, XMMRegister src1, XMMRegister src2, XMMRegister xtmp1, XMMRegister xtmp2, int vlen_enc); @@ -576,12 +579,22 @@ public: void evfp16ph(int opcode, XMMRegister dst, XMMRegister src1, Address src2, int vlen_enc); - void vector_max_min_fp16(int opcode, XMMRegister dst, XMMRegister src1, XMMRegister src2, - KRegister ktmp, XMMRegister xtmp1, XMMRegister xtmp2, int vlen_enc); + void vminmax_fp16(int opcode, XMMRegister dst, XMMRegister src1, XMMRegister src2, + KRegister ktmp, XMMRegister xtmp1, XMMRegister xtmp2, int vlen_enc); - void scalar_max_min_fp16(int opcode, XMMRegister dst, XMMRegister src1, XMMRegister src2, - KRegister ktmp, XMMRegister xtmp1, XMMRegister xtmp2); + void vminmax_fp16_avx10_2(int opcode, XMMRegister dst, XMMRegister src1, XMMRegister src2, + KRegister ktmp, int vlen_enc); + + void vminmax_fp16_avx10_2(int opcode, XMMRegister dst, XMMRegister src1, Address src2, + KRegister ktmp, int vlen_enc); + + void sminmax_fp16(int opcode, XMMRegister dst, XMMRegister src1, XMMRegister src2, + KRegister ktmp, XMMRegister xtmp1, XMMRegister xtmp2); + + void sminmax_fp16_avx10_2(int opcode, XMMRegister dst, XMMRegister src1, XMMRegister src2, + KRegister ktmp); void reconstruct_frame_pointer(Register rtmp); + int vector_iota_entry_index(BasicType bt); #endif // CPU_X86_C2_MACROASSEMBLER_X86_HPP diff --git a/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.cpp index 0ea769dd488..c05f37a3bea 100644 --- a/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -130,7 +130,7 @@ __ BIND(L_loop); __ BIND(L_done); } -void CardTableBarrierSetAssembler::store_check(MacroAssembler* masm, Register obj, Address dst, Register rscratch) { +void CardTableBarrierSetAssembler::store_check(MacroAssembler* masm, Register obj, Register rscratch) { // Does a store check for the oop in register obj. The content of // register obj is destroyed afterwards. CardTableBarrierSet* ctbs = CardTableBarrierSet::barrier_set(); @@ -138,6 +138,8 @@ void CardTableBarrierSetAssembler::store_check(MacroAssembler* masm, Register ob __ shrptr(obj, CardTable::card_shift()); Address card_addr; + precond(rscratch != noreg); + assert_different_registers(obj, rscratch); // The calculation for byte_map_base is as follows: // byte_map_base = _byte_map - (uintptr_t(low_bound) >> card_shift); @@ -161,7 +163,7 @@ void CardTableBarrierSetAssembler::store_check(MacroAssembler* masm, Register ob // entry and that entry is not properly handled by the relocation code. AddressLiteral cardtable((address)byte_map_base, relocInfo::none); Address index(noreg, obj, Address::times_1); - card_addr = __ as_Address(ArrayAddress(cardtable, index), rscratch1); + card_addr = __ as_Address(ArrayAddress(cardtable, index), rscratch); } int dirty = CardTable::dirty_card_val(); @@ -190,10 +192,10 @@ void CardTableBarrierSetAssembler::oop_store_at(MacroAssembler* masm, DecoratorS if (needs_post_barrier) { // flatten object address if needed if (!precise || (dst.index() == noreg && dst.disp() == 0)) { - store_check(masm, dst.base(), dst, tmp2); + store_check(masm, dst.base(), tmp2); } else { __ lea(tmp1, dst); - store_check(masm, tmp1, dst, tmp2); + store_check(masm, tmp1, tmp2); } } } diff --git a/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.hpp b/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.hpp index 201c11062f2..c38e16d4d5f 100644 --- a/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -33,7 +33,7 @@ protected: virtual void gen_write_ref_array_pre_barrier(MacroAssembler* masm, DecoratorSet decorators, Register addr, Register count) {} - void store_check(MacroAssembler* masm, Register obj, Address dst, Register rscratch); + void store_check(MacroAssembler* masm, Register obj, Register rscratch); virtual void gen_write_ref_array_post_barrier(MacroAssembler* masm, DecoratorSet decorators, Register addr, Register count, Register tmp); diff --git a/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp index 47a3dad54e7..c20551b5084 100644 --- a/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp @@ -31,6 +31,7 @@ #include "gc/z/zBarrierSetAssembler.hpp" #include "gc/z/zBarrierSetRuntime.hpp" #include "gc/z/zThreadLocalData.hpp" +#include "logging/log.hpp" #include "memory/resourceArea.hpp" #include "runtime/jniHandles.hpp" #include "runtime/sharedRuntime.hpp" @@ -1391,10 +1392,13 @@ static uint16_t patch_barrier_relocation_value(int format) { } } -void ZBarrierSetAssembler::patch_barrier_relocation(address addr, int format) { +void ZBarrierSetAssembler::patch_barrier_relocation(address addr, int format, bool log) { const int offset = patch_barrier_relocation_offset(format); const uint16_t value = patch_barrier_relocation_value(format); uint8_t* const patch_addr = (uint8_t*)addr + offset; + if (log) { + log_trace(aot, codecache, stubs)("patching address " INTPTR_FORMAT " offset %d value 0x%x", p2i(addr), offset, value); + } if (format == ZBarrierRelocationFormatLoadGoodBeforeShl) { if (VM_Version::supports_apx_f()) { NativeInstruction* instruction = nativeInstruction_at(addr); @@ -1426,6 +1430,74 @@ void ZBarrierSetAssembler::patch_barriers() { #undef __ #define __ masm-> +void ZBarrierSetAssembler::register_reloc_addresses(GrowableArray
&entries, int begin, int count) { + int formats[] = { + ZBarrierRelocationFormatLoadBadAfterTest, + ZBarrierRelocationFormatStoreBadAfterTest, + ZBarrierRelocationFormatStoreGoodAfterOr, + -1 + }; + int format_idx = 0; + int format = formats[format_idx++]; + for (int i = begin; i < begin + count; i++) { + address addr = entries.at(i); + // reloc addresses occur in 3 groups terminated with a nullptr + if (addr == nullptr) { + assert(format_idx < (int)(sizeof(formats) / sizeof(formats[0])), + "too many reloc groups"); + format = formats[format_idx++]; + } else { + switch(format) { + case ZBarrierRelocationFormatLoadBadAfterTest: + _load_bad_relocations.append(addr); + break; + case ZBarrierRelocationFormatStoreBadAfterTest: + _store_bad_relocations.append(addr); + break; + case ZBarrierRelocationFormatStoreGoodAfterOr: + _store_good_relocations.append(addr); + break; + default: + ShouldNotReachHere(); + break; + } + patch_barrier_relocation(addr, format, true); + } + } + assert(format == -1, "unterminated format list"); +} + +void ZBarrierSetAssembler::retrieve_reloc_addresses(address start, address end, GrowableArray
&entries) { + assert(start != nullptr, "start address must not be null"); + assert(end != nullptr, "start address must not be null"); + assert(start < end, "stub range must not be empty"); + for (int i = 0; i < _load_bad_relocations.length(); i++) { + address addr = _load_bad_relocations.at(i); + assert(addr != nullptr, "load bad reloc address shoudl not be null!"); + if (start <= addr && addr < end) { + entries.append(addr); + } + } + entries.append(nullptr); + for (int i = 0; i < _store_bad_relocations.length(); i++) { + address addr = _store_bad_relocations.at(i); + assert(addr != nullptr, "store bad reloc address shoudl not be null!"); + if (start <= addr && addr < end) { + entries.append(addr); + } + } + entries.append(nullptr); + for (int i = 0; i < _store_good_relocations.length(); i++) { + address addr = _store_good_relocations.at(i); + assert(addr != nullptr, "store good reloc address shoudl not be null!"); + if (start <= addr && addr < end) { + entries.append(addr); + } + } + entries.append(nullptr); +} + + void ZBarrierSetAssembler::check_oop(MacroAssembler* masm, Register obj, Register tmp1, Register tmp2, Label& error) { // C1 calls verfy_oop in the middle of barriers, before they have been uncolored // and after being colored. Therefore, we must deal with colored oops as well. diff --git a/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.hpp b/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.hpp index e91e2b9ea20..ce0c4769716 100644 --- a/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.hpp @@ -189,10 +189,14 @@ public: Label& slow_path, Label& slow_path_continuation) const; - void patch_barrier_relocation(address addr, int format); + void patch_barrier_relocation(address addr, int format, bool log = false); void patch_barriers(); + void register_reloc_addresses(GrowableArray
&entries, int begin, int count); + + void retrieve_reloc_addresses(address start, address end, GrowableArray
&entries); + void check_oop(MacroAssembler* masm, Register obj, Register tmp1, Register tmp2, Label& error); }; diff --git a/src/hotspot/cpu/x86/interp_masm_x86.cpp b/src/hotspot/cpu/x86/interp_masm_x86.cpp index b2ea4143ac4..a38971c86fb 100644 --- a/src/hotspot/cpu/x86/interp_masm_x86.cpp +++ b/src/hotspot/cpu/x86/interp_masm_x86.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -1392,28 +1392,15 @@ void InterpreterMacroAssembler::profile_final_call(Register mdp) { void InterpreterMacroAssembler::profile_virtual_call(Register receiver, - Register mdp, - bool receiver_can_be_null) { + Register mdp) { if (ProfileInterpreter) { Label profile_continue; // If no method data exists, go to profile_continue. test_method_data_pointer(mdp, profile_continue); - Label skip_receiver_profile; - if (receiver_can_be_null) { - Label not_null; - testptr(receiver, receiver); - jccb(Assembler::notZero, not_null); - // We are making a call. Increment the count for null receiver. - increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset())); - jmp(skip_receiver_profile); - bind(not_null); - } - // Record the receiver type. profile_receiver_type(receiver, mdp, 0); - bind(skip_receiver_profile); // The method data pointer needs to be updated to reflect the new target. update_mdp_by_constant(mdp, in_bytes(VirtualCallData::virtual_call_data_size())); diff --git a/src/hotspot/cpu/x86/interp_masm_x86.hpp b/src/hotspot/cpu/x86/interp_masm_x86.hpp index 4114028f78e..dfbd7ab64e0 100644 --- a/src/hotspot/cpu/x86/interp_masm_x86.hpp +++ b/src/hotspot/cpu/x86/interp_masm_x86.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -243,8 +243,7 @@ class InterpreterMacroAssembler: public MacroAssembler { void profile_not_taken_branch(Register mdp); void profile_call(Register mdp); void profile_final_call(Register mdp); - void profile_virtual_call(Register receiver, Register mdp, - bool receiver_can_be_null = false); + void profile_virtual_call(Register receiver, Register mdp); void profile_ret(Register return_bci, Register mdp); void profile_null_seen(Register mdp); void profile_typecheck(Register mdp, Register klass); diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp index 1d77be26bd9..5ab3ca339aa 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp @@ -385,7 +385,8 @@ void MacroAssembler::warn(const char* msg) { // Windows always allocates space for its register args subq(rsp, frame::arg_reg_save_area_bytes); #endif - lea(c_rarg0, ExternalAddress((address) msg)); + const char* str = (code_section()->scratch_emit()) ? msg : AOTCodeCache::add_C_string(msg); + lea(c_rarg0, ExternalAddress((address) str)); call(RuntimeAddress(CAST_FROM_FN_PTR(address, warning))); #ifdef _WIN64 @@ -985,12 +986,9 @@ int MacroAssembler::ic_check(int end_alignment) { if (UseCompactObjectHeaders) { load_narrow_klass_compact(temp, receiver); cmpl(temp, Address(data, CompiledICData::speculated_klass_offset())); - } else if (UseCompressedClassPointers) { + } else { movl(temp, Address(receiver, oopDesc::klass_offset_in_bytes())); cmpl(temp, Address(data, CompiledICData::speculated_klass_offset())); - } else { - movptr(temp, Address(receiver, oopDesc::klass_offset_in_bytes())); - cmpptr(temp, Address(data, CompiledICData::speculated_klass_offset())); } // if inline cache check fails, then jump to runtime routine @@ -1961,6 +1959,16 @@ void MacroAssembler::movflt(XMMRegister dst, AddressLiteral src, Register rscrat } } +void MacroAssembler::movhlf(XMMRegister dst, XMMRegister src, Register rscratch) { + if (VM_Version::supports_avx10_2()) { + evmovw(dst, src); + } else { + assert(rscratch != noreg, "missing"); + evmovw(rscratch, src); + evmovw(dst, rscratch); + } +} + void MacroAssembler::mov64(Register dst, int64_t imm64) { if (is_uimm32(imm64)) { movl(dst, checked_cast(imm64)); @@ -2664,14 +2672,14 @@ void MacroAssembler::ucomisd(XMMRegister dst, AddressLiteral src, Register rscra } } -void MacroAssembler::vucomxsd(XMMRegister dst, AddressLiteral src, Register rscratch) { +void MacroAssembler::evucomxsd(XMMRegister dst, AddressLiteral src, Register rscratch) { assert(rscratch != noreg || always_reachable(src), "missing"); if (reachable(src)) { - Assembler::vucomxsd(dst, as_Address(src)); + Assembler::evucomxsd(dst, as_Address(src)); } else { lea(rscratch, src); - Assembler::vucomxsd(dst, Address(rscratch, 0)); + Assembler::evucomxsd(dst, Address(rscratch, 0)); } } @@ -2686,14 +2694,36 @@ void MacroAssembler::ucomiss(XMMRegister dst, AddressLiteral src, Register rscra } } -void MacroAssembler::vucomxss(XMMRegister dst, AddressLiteral src, Register rscratch) { +void MacroAssembler::evucomxss(XMMRegister dst, AddressLiteral src, Register rscratch) { assert(rscratch != noreg || always_reachable(src), "missing"); if (reachable(src)) { - Assembler::vucomxss(dst, as_Address(src)); + Assembler::evucomxss(dst, as_Address(src)); } else { lea(rscratch, src); - Assembler::vucomxss(dst, Address(rscratch, 0)); + Assembler::evucomxss(dst, Address(rscratch, 0)); + } +} + +void MacroAssembler::evucomish(XMMRegister dst, AddressLiteral src, Register rscratch) { + assert(rscratch != noreg || always_reachable(src), "missing"); + + if (reachable(src)) { + Assembler::evucomish(dst, as_Address(src)); + } else { + lea(rscratch, src); + Assembler::evucomish(dst, Address(rscratch, 0)); + } +} + +void MacroAssembler::evucomxsh(XMMRegister dst, AddressLiteral src, Register rscratch) { + assert(rscratch != noreg || always_reachable(src), "missing"); + + if (reachable(src)) { + Assembler::evucomxsh(dst, as_Address(src)); + } else { + lea(rscratch, src); + Assembler::evucomxsh(dst, Address(rscratch, 0)); } } @@ -5384,11 +5414,9 @@ void MacroAssembler::load_klass(Register dst, Register src, Register tmp) { if (UseCompactObjectHeaders) { load_narrow_klass_compact(dst, src); decode_klass_not_null(dst, tmp); - } else if (UseCompressedClassPointers) { + } else { movl(dst, Address(src, oopDesc::klass_offset_in_bytes())); decode_klass_not_null(dst, tmp); - } else { - movptr(dst, Address(src, oopDesc::klass_offset_in_bytes())); } } @@ -5396,12 +5424,8 @@ void MacroAssembler::store_klass(Register dst, Register src, Register tmp) { assert(!UseCompactObjectHeaders, "not with compact headers"); assert_different_registers(src, tmp); assert_different_registers(dst, tmp); - if (UseCompressedClassPointers) { - encode_klass_not_null(src, tmp); - movl(Address(dst, oopDesc::klass_offset_in_bytes()), src); - } else { - movptr(Address(dst, oopDesc::klass_offset_in_bytes()), src); - } + encode_klass_not_null(src, tmp); + movl(Address(dst, oopDesc::klass_offset_in_bytes()), src); } void MacroAssembler::cmp_klass(Register klass, Register obj, Register tmp) { @@ -5410,10 +5434,8 @@ void MacroAssembler::cmp_klass(Register klass, Register obj, Register tmp) { assert_different_registers(klass, obj, tmp); load_narrow_klass_compact(tmp, obj); cmpl(klass, tmp); - } else if (UseCompressedClassPointers) { - cmpl(klass, Address(obj, oopDesc::klass_offset_in_bytes())); } else { - cmpptr(klass, Address(obj, oopDesc::klass_offset_in_bytes())); + cmpl(klass, Address(obj, oopDesc::klass_offset_in_bytes())); } } @@ -5424,12 +5446,9 @@ void MacroAssembler::cmp_klasses_from_objects(Register obj1, Register obj2, Regi load_narrow_klass_compact(tmp1, obj1); load_narrow_klass_compact(tmp2, obj2); cmpl(tmp1, tmp2); - } else if (UseCompressedClassPointers) { + } else { movl(tmp1, Address(obj1, oopDesc::klass_offset_in_bytes())); cmpl(tmp1, Address(obj2, oopDesc::klass_offset_in_bytes())); - } else { - movptr(tmp1, Address(obj1, oopDesc::klass_offset_in_bytes())); - cmpptr(tmp1, Address(obj2, oopDesc::klass_offset_in_bytes())); } } @@ -5478,10 +5497,8 @@ void MacroAssembler::store_heap_oop_null(Address dst) { void MacroAssembler::store_klass_gap(Register dst, Register src) { assert(!UseCompactObjectHeaders, "Don't use with compact headers"); - if (UseCompressedClassPointers) { - // Store to klass gap in destination - movl(Address(dst, oopDesc::klass_gap_offset_in_bytes()), src); - } + // Store to klass gap in destination + movl(Address(dst, oopDesc::klass_gap_offset_in_bytes()), src); } #ifdef ASSERT @@ -5656,7 +5673,12 @@ void MacroAssembler::encode_and_move_klass_not_null(Register dst, Register src) BLOCK_COMMENT("encode_and_move_klass_not_null {"); assert_different_registers(src, dst); if (CompressedKlassPointers::base() != nullptr) { - movptr(dst, -(intptr_t)CompressedKlassPointers::base()); + if (AOTCodeCache::is_on_for_dump()) { + movptr(dst, ExternalAddress(CompressedKlassPointers::base_addr())); + negq(dst); + } else { + movptr(dst, -(intptr_t)CompressedKlassPointers::base()); + } addq(dst, src); } else { movptr(dst, src); @@ -5671,7 +5693,6 @@ void MacroAssembler::decode_klass_not_null(Register r, Register tmp) { BLOCK_COMMENT("decode_klass_not_null {"); assert_different_registers(r, tmp); // Note: it will change flags - assert(UseCompressedClassPointers, "should only be used for compressed headers"); // Cannot assert, unverified entry point counts instructions (see .ad file) // vtableStubs also counts instructions in pd_code_size_limit. // Also do not verify_oop as this is called by verify_oop. @@ -5693,7 +5714,6 @@ void MacroAssembler::decode_and_move_klass_not_null(Register dst, Register src) BLOCK_COMMENT("decode_and_move_klass_not_null {"); assert_different_registers(src, dst); // Note: it will change flags - assert (UseCompressedClassPointers, "should only be used for compressed headers"); // Cannot assert, unverified entry point counts instructions (see .ad file) // vtableStubs also counts instructions in pd_code_size_limit. // Also do not verify_oop as this is called by verify_oop. @@ -5706,7 +5726,11 @@ void MacroAssembler::decode_and_move_klass_not_null(Register dst, Register src) } else { if (CompressedKlassPointers::shift() <= Address::times_8) { if (CompressedKlassPointers::base() != nullptr) { - movptr(dst, (intptr_t)CompressedKlassPointers::base()); + if (AOTCodeCache::is_on_for_dump()) { + movptr(dst, ExternalAddress(CompressedKlassPointers::base_addr())); + } else { + movptr(dst, (intptr_t)CompressedKlassPointers::base()); + } } else { xorq(dst, dst); } @@ -5718,9 +5742,14 @@ void MacroAssembler::decode_and_move_klass_not_null(Register dst, Register src) } } else { if (CompressedKlassPointers::base() != nullptr) { - const intptr_t base_right_shifted = - (intptr_t)CompressedKlassPointers::base() >> CompressedKlassPointers::shift(); - movptr(dst, base_right_shifted); + if (AOTCodeCache::is_on_for_dump()) { + movptr(dst, ExternalAddress(CompressedKlassPointers::base_addr())); + shrq(dst, CompressedKlassPointers::shift()); + } else { + const intptr_t base_right_shifted = + (intptr_t)CompressedKlassPointers::base() >> CompressedKlassPointers::shift(); + movptr(dst, base_right_shifted); + } } else { xorq(dst, dst); } @@ -5750,7 +5779,6 @@ void MacroAssembler::set_narrow_oop(Address dst, jobject obj) { } void MacroAssembler::set_narrow_klass(Register dst, Klass* k) { - assert (UseCompressedClassPointers, "should only be used for compressed headers"); assert (oop_recorder() != nullptr, "this assembler needs an OopRecorder"); int klass_index = oop_recorder()->find_index(k); RelocationHolder rspec = metadata_Relocation::spec(klass_index); @@ -5758,7 +5786,6 @@ void MacroAssembler::set_narrow_klass(Register dst, Klass* k) { } void MacroAssembler::set_narrow_klass(Address dst, Klass* k) { - assert (UseCompressedClassPointers, "should only be used for compressed headers"); assert (oop_recorder() != nullptr, "this assembler needs an OopRecorder"); int klass_index = oop_recorder()->find_index(k); RelocationHolder rspec = metadata_Relocation::spec(klass_index); @@ -5784,7 +5811,6 @@ void MacroAssembler::cmp_narrow_oop(Address dst, jobject obj) { } void MacroAssembler::cmp_narrow_klass(Register dst, Klass* k) { - assert (UseCompressedClassPointers, "should only be used for compressed headers"); assert (oop_recorder() != nullptr, "this assembler needs an OopRecorder"); int klass_index = oop_recorder()->find_index(k); RelocationHolder rspec = metadata_Relocation::spec(klass_index); @@ -5792,7 +5818,6 @@ void MacroAssembler::cmp_narrow_klass(Register dst, Klass* k) { } void MacroAssembler::cmp_narrow_klass(Address dst, Klass* k) { - assert (UseCompressedClassPointers, "should only be used for compressed headers"); assert (oop_recorder() != nullptr, "this assembler needs an OopRecorder"); int klass_index = oop_recorder()->find_index(k); RelocationHolder rspec = metadata_Relocation::spec(klass_index); @@ -5801,7 +5826,7 @@ void MacroAssembler::cmp_narrow_klass(Address dst, Klass* k) { void MacroAssembler::reinit_heapbase() { if (UseCompressedOops) { - if (Universe::heap() != nullptr) { + if (Universe::heap() != nullptr && !AOTCodeCache::is_on_for_dump()) { if (CompressedOops::base() == nullptr) { MacroAssembler::xorptr(r12_heapbase, r12_heapbase); } else { @@ -9185,7 +9210,7 @@ void MacroAssembler::evpmaxs(BasicType type, XMMRegister dst, KRegister mask, XM case T_FLOAT: evminmaxps(dst, mask, nds, src, merge, AVX10_2_MINMAX_MAX_COMPARE_SIGN, vector_len); break; case T_DOUBLE: - evminmaxps(dst, mask, nds, src, merge, AVX10_2_MINMAX_MAX_COMPARE_SIGN, vector_len); break; + evminmaxpd(dst, mask, nds, src, merge, AVX10_2_MINMAX_MAX_COMPARE_SIGN, vector_len); break; default: fatal("Unexpected type argument %s", type2name(type)); break; } diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.hpp b/src/hotspot/cpu/x86/macroAssembler_x86.hpp index 8469deaa8be..021d2943ee8 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.hpp @@ -162,6 +162,8 @@ class MacroAssembler: public Assembler { void incrementq(AddressLiteral dst, Register rscratch = noreg); + void movhlf(XMMRegister dst, XMMRegister src, Register rscratch = noreg); + // Support optimal SSE move instructions. void movflt(XMMRegister dst, XMMRegister src) { if (dst-> encoding() == src->encoding()) return; @@ -351,8 +353,7 @@ class MacroAssembler: public Assembler { void load_klass(Register dst, Register src, Register tmp); void store_klass(Register dst, Register src, Register tmp); - // Compares the Klass pointer of an object to a given Klass (which might be narrow, - // depending on UseCompressedClassPointers). + // Compares the narrow Klass pointer of an object to a given narrow Klass. void cmp_klass(Register klass, Register obj, Register tmp); // Compares the Klass pointer of two objects obj1 and obj2. Result is in the condition flags. @@ -1309,21 +1310,29 @@ public: void subss(XMMRegister dst, Address src) { Assembler::subss(dst, src); } void subss(XMMRegister dst, AddressLiteral src, Register rscratch = noreg); + void evucomish(XMMRegister dst, XMMRegister src) { Assembler::evucomish(dst, src); } + void evucomish(XMMRegister dst, Address src) { Assembler::evucomish(dst, src); } + void evucomish(XMMRegister dst, AddressLiteral src, Register rscratch = noreg); + + void evucomxsh(XMMRegister dst, XMMRegister src) { Assembler::evucomxsh(dst, src); } + void evucomxsh(XMMRegister dst, Address src) { Assembler::evucomxsh(dst, src); } + void evucomxsh(XMMRegister dst, AddressLiteral src, Register rscratch = noreg); + void ucomiss(XMMRegister dst, XMMRegister src) { Assembler::ucomiss(dst, src); } void ucomiss(XMMRegister dst, Address src) { Assembler::ucomiss(dst, src); } void ucomiss(XMMRegister dst, AddressLiteral src, Register rscratch = noreg); - void vucomxss(XMMRegister dst, XMMRegister src) { Assembler::vucomxss(dst, src); } - void vucomxss(XMMRegister dst, Address src) { Assembler::vucomxss(dst, src); } - void vucomxss(XMMRegister dst, AddressLiteral src, Register rscratch = noreg); + void evucomxss(XMMRegister dst, XMMRegister src) { Assembler::evucomxss(dst, src); } + void evucomxss(XMMRegister dst, Address src) { Assembler::evucomxss(dst, src); } + void evucomxss(XMMRegister dst, AddressLiteral src, Register rscratch = noreg); void ucomisd(XMMRegister dst, XMMRegister src) { Assembler::ucomisd(dst, src); } void ucomisd(XMMRegister dst, Address src) { Assembler::ucomisd(dst, src); } void ucomisd(XMMRegister dst, AddressLiteral src, Register rscratch = noreg); - void vucomxsd(XMMRegister dst, XMMRegister src) { Assembler::vucomxsd(dst, src); } - void vucomxsd(XMMRegister dst, Address src) { Assembler::vucomxsd(dst, src); } - void vucomxsd(XMMRegister dst, AddressLiteral src, Register rscratch = noreg); + void evucomxsd(XMMRegister dst, XMMRegister src) { Assembler::evucomxsd(dst, src); } + void evucomxsd(XMMRegister dst, Address src) { Assembler::evucomxsd(dst, src); } + void evucomxsd(XMMRegister dst, AddressLiteral src, Register rscratch = noreg); // Bitwise Logical XOR of Packed Double-Precision Floating-Point Values void xorpd(XMMRegister dst, XMMRegister src); diff --git a/src/hotspot/cpu/x86/macroAssembler_x86_sha.cpp b/src/hotspot/cpu/x86/macroAssembler_x86_sha.cpp index 9f0232075cd..401d5dc22cc 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86_sha.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86_sha.cpp @@ -242,7 +242,6 @@ void MacroAssembler::fast_sha256(XMMRegister msg, XMMRegister state0, XMMRegiste Label done_hash, loop0; address K256 = StubRoutines::x86::k256_addr(); - address pshuffle_byte_flip_mask = StubRoutines::x86::pshuffle_byte_flip_mask_addr(); movdqu(state0, Address(state, 0)); movdqu(state1, Address(state, 16)); @@ -253,7 +252,7 @@ void MacroAssembler::fast_sha256(XMMRegister msg, XMMRegister state0, XMMRegiste palignr(state0, state1, 8); pblendw(state1, msgtmp4, 0xF0); - movdqu(shuf_mask, ExternalAddress(pshuffle_byte_flip_mask)); + movdqu(shuf_mask, ExternalAddress(StubRoutines::x86::pshuffle_byte_flip_mask_addr())); lea(rax, ExternalAddress(K256)); bind(loop0); @@ -661,8 +660,6 @@ void MacroAssembler::sha256_AVX2(XMMRegister msg, XMMRegister state0, XMMRegiste compute_size1, compute_size_end1; address K256_W = StubRoutines::x86::k256_W_addr(); - address pshuffle_byte_flip_mask = StubRoutines::x86::pshuffle_byte_flip_mask_addr(); - address pshuffle_byte_flip_mask_addr = nullptr; const XMMRegister& SHUF_00BA = xmm10; // ymm10: shuffle xBxA -> 00BA const XMMRegister& SHUF_DC00 = xmm12; // ymm12: shuffle xDxC -> DC00 @@ -791,10 +788,14 @@ enum { // load g - r10 after it is used as scratch movl(h, Address(CTX, 4*7)); - pshuffle_byte_flip_mask_addr = pshuffle_byte_flip_mask; - vmovdqu(BYTE_FLIP_MASK, ExternalAddress(pshuffle_byte_flip_mask_addr + 0)); // [PSHUFFLE_BYTE_FLIP_MASK wrt rip] - vmovdqu(SHUF_00BA, ExternalAddress(pshuffle_byte_flip_mask_addr + 32)); // [_SHUF_00BA wrt rip] - vmovdqu(SHUF_DC00, ExternalAddress(pshuffle_byte_flip_mask_addr + 64)); // [_SHUF_DC00 wrt rip] + // the three successive pshuffle_byte_flip_mask stub entries should + // be offset by 32 bytes + assert(StubRoutines::x86::pshuffle_byte_flip_mask_addr() + 32 == StubRoutines::x86::pshuffle_byte_flip_mask_00ba_addr(), "sanity"); + assert(StubRoutines::x86::pshuffle_byte_flip_mask_addr() + 64 == StubRoutines::x86::pshuffle_byte_flip_mask_dc00_addr(), "sanity"); + + vmovdqu(BYTE_FLIP_MASK, ExternalAddress(StubRoutines::x86::pshuffle_byte_flip_mask_addr())); // [PSHUFFLE_BYTE_FLIP_MASK wrt rip] + vmovdqu(SHUF_00BA, ExternalAddress(StubRoutines::x86::pshuffle_byte_flip_mask_00ba_addr())); // [_SHUF_00BA wrt rip] + vmovdqu(SHUF_DC00, ExternalAddress(StubRoutines::x86::pshuffle_byte_flip_mask_dc00_addr())); // [_SHUF_DC00 wrt rip] movl(g, Address(CTX, 4*6)); @@ -953,11 +954,9 @@ bind(only_one_block); // load g - r10 after use as scratch movl(h, Address(CTX, 4*7)); // 0x5be0cd19 - - pshuffle_byte_flip_mask_addr = pshuffle_byte_flip_mask; - vmovdqu(BYTE_FLIP_MASK, ExternalAddress(pshuffle_byte_flip_mask_addr + 0)); // [PSHUFFLE_BYTE_FLIP_MASK wrt rip] - vmovdqu(SHUF_00BA, ExternalAddress(pshuffle_byte_flip_mask_addr + 32)); // [_SHUF_00BA wrt rip] - vmovdqu(SHUF_DC00, ExternalAddress(pshuffle_byte_flip_mask_addr + 64)); // [_SHUF_DC00 wrt rip] + vmovdqu(BYTE_FLIP_MASK, ExternalAddress(StubRoutines::x86::pshuffle_byte_flip_mask_addr())); // [PSHUFFLE_BYTE_FLIP_MASK wrt rip] + vmovdqu(SHUF_00BA, ExternalAddress(StubRoutines::x86::pshuffle_byte_flip_mask_00ba_addr())); // [_SHUF_00BA wrt rip] + vmovdqu(SHUF_DC00, ExternalAddress(StubRoutines::x86::pshuffle_byte_flip_mask_dc00_addr())); // [_SHUF_DC00 wrt rip] movl(g, Address(CTX, 4*6)); // 0x1f83d9ab @@ -1346,9 +1345,12 @@ void MacroAssembler::sha512_AVX2(XMMRegister msg, XMMRegister state0, XMMRegiste // load g - r10 after it is used as scratch movq(h, Address(CTX, 8 * 7)); - pshuffle_byte_flip_mask_addr = pshuffle_byte_flip_mask_sha512; - vmovdqu(BYTE_FLIP_MASK, ExternalAddress(pshuffle_byte_flip_mask_addr + 0)); // PSHUFFLE_BYTE_FLIP_MASK wrt rip - vmovdqu(YMM_MASK_LO, ExternalAddress(pshuffle_byte_flip_mask_addr + 32)); + // the two successive pshuffle_byte_flip_mask_sha512 stub entries should + // be offset by 32 bytes + assert(StubRoutines::x86::pshuffle_byte_flip_mask_addr_sha512() + 32 == StubRoutines::x86::pshuffle_byte_flip_mask_ymm_lo_addr_sha512(), "sanity"); + + vmovdqu(BYTE_FLIP_MASK, ExternalAddress(StubRoutines::x86::pshuffle_byte_flip_mask_addr_sha512())); // PSHUFFLE_BYTE_FLIP_MASK wrt rip + vmovdqu(YMM_MASK_LO, ExternalAddress(StubRoutines::x86::pshuffle_byte_flip_mask_ymm_lo_addr_sha512())); // MASK_YMM_LO wrt rip movq(g, Address(CTX, 8 * 6)); diff --git a/src/hotspot/cpu/x86/matcher_x86.hpp b/src/hotspot/cpu/x86/matcher_x86.hpp index f7973a8564e..62a5d2827bc 100644 --- a/src/hotspot/cpu/x86/matcher_x86.hpp +++ b/src/hotspot/cpu/x86/matcher_x86.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -75,7 +75,6 @@ } static bool narrow_klass_use_complex_address() { - assert(UseCompressedClassPointers, "only for compressed klass code"); return (CompressedKlassPointers::shift() <= 3); } diff --git a/src/hotspot/cpu/x86/methodHandles_x86.cpp b/src/hotspot/cpu/x86/methodHandles_x86.cpp index 54376c6ad9a..5b15444bc32 100644 --- a/src/hotspot/cpu/x86/methodHandles_x86.cpp +++ b/src/hotspot/cpu/x86/methodHandles_x86.cpp @@ -110,14 +110,13 @@ void MethodHandles::verify_ref_kind(MacroAssembler* _masm, int ref_kind, Registe __ andl(temp, java_lang_invoke_MemberName::MN_REFERENCE_KIND_MASK); __ cmpl(temp, ref_kind); __ jcc(Assembler::equal, L); - { char* buf = NEW_C_HEAP_ARRAY(char, 100, mtInternal); - jio_snprintf(buf, 100, "verify_ref_kind expected %x", ref_kind); - if (ref_kind == JVM_REF_invokeVirtual || - ref_kind == JVM_REF_invokeSpecial) - // could do this for all ref_kinds, but would explode assembly code size - trace_method_handle(_masm, buf); - __ STOP(buf); + const char* msg = ref_kind_to_verify_msg(ref_kind); + if (ref_kind == JVM_REF_invokeVirtual || + ref_kind == JVM_REF_invokeSpecial) { + // could do this for all ref_kinds, but would explode assembly code size + trace_method_handle(_masm, msg); } + __ STOP(msg); BLOCK_COMMENT("} verify_ref_kind"); __ bind(L); } diff --git a/src/hotspot/cpu/x86/stubDeclarations_x86.hpp b/src/hotspot/cpu/x86/stubDeclarations_x86.hpp index 971c8fd3c44..24886deb3c5 100644 --- a/src/hotspot/cpu/x86/stubDeclarations_x86.hpp +++ b/src/hotspot/cpu/x86/stubDeclarations_x86.hpp @@ -29,14 +29,16 @@ #define STUBGEN_PREUNIVERSE_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(preuniverse, 500) \ #define STUBGEN_INITIAL_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(initial, PRODUCT_ONLY(20000) NOT_PRODUCT(21000) WINDOWS_ONLY(+1000)) \ do_stub(initial, verify_mxcsr) \ do_arch_entry(x86, initial, verify_mxcsr, verify_mxcsr_entry, \ @@ -65,14 +67,18 @@ #define STUBGEN_CONTINUATION_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(continuation, 3000) \ +// count needed for declaration of vector_iota_indices stub +#define VECTOR_IOTA_COUNT 6 #define STUBGEN_COMPILER_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(compiler, 120000 WINDOWS_ONLY(+2000)) \ do_stub(compiler, vector_float_sign_mask) \ do_arch_entry(x86, compiler, vector_float_sign_mask, \ @@ -126,8 +132,9 @@ do_arch_entry(x86, compiler, vector_long_sign_mask, \ vector_long_sign_mask, vector_long_sign_mask) \ do_stub(compiler, vector_iota_indices) \ - do_arch_entry(x86, compiler, vector_iota_indices, \ - vector_iota_indices, vector_iota_indices) \ + do_arch_entry_array(x86, compiler, vector_iota_indices, \ + vector_iota_indices, vector_iota_indices, \ + VECTOR_IOTA_COUNT) \ do_stub(compiler, vector_count_leading_zeros_lut) \ do_arch_entry(x86, compiler, vector_count_leading_zeros_lut, \ vector_count_leading_zeros_lut, \ @@ -161,6 +168,12 @@ do_arch_entry(x86, compiler, pshuffle_byte_flip_mask, \ pshuffle_byte_flip_mask_addr, \ pshuffle_byte_flip_mask_addr) \ + do_arch_entry(x86, compiler, pshuffle_byte_flip_mask, \ + pshuffle_byte_flip_mask_00ba_addr, \ + pshuffle_byte_flip_mask_00ba_addr) \ + do_arch_entry(x86, compiler, pshuffle_byte_flip_mask, \ + pshuffle_byte_flip_mask_dc00_addr, \ + pshuffle_byte_flip_mask_dc00_addr) \ /* x86_64 exposes these 3 stubs via a generic entry array */ \ /* other arches use arch-specific entries */ \ /* this really needs rationalising */ \ @@ -171,6 +184,9 @@ do_arch_entry(x86, compiler, pshuffle_byte_flip_mask_sha512, \ pshuffle_byte_flip_mask_addr_sha512, \ pshuffle_byte_flip_mask_addr_sha512) \ + do_arch_entry(x86, compiler, pshuffle_byte_flip_mask_sha512, \ + pshuffle_byte_flip_mask_ymm_lo_addr_sha512, \ + pshuffle_byte_flip_mask_ymm_lo_addr_sha512) \ do_stub(compiler, compress_perm_table32) \ do_arch_entry(x86, compiler, compress_perm_table32, \ compress_perm_table32, compress_perm_table32) \ @@ -241,7 +257,8 @@ #define STUBGEN_FINAL_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(final, 33000 \ WINDOWS_ONLY(+22000) ZGC_ONLY(+20000)) \ diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp index efb0411aa39..993d1964034 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp @@ -188,8 +188,18 @@ address StubGenerator::generate_call_stub(address& return_address) { (int)frame::entry_frame_call_wrapper_offset == (int)call_wrapper_off, "adjust this code"); StubId stub_id = StubId::stubgen_call_stub_id; + GrowableArray
entries; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 2, "sanity check"); + address start = load_archive_data(stub_id, &entries); + if (start != nullptr) { + assert(entries.length() == 1, "expected 1 extra entry"); + return_address = entries.at(0); + return start; + } + StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); // same as in generate_catch_exception()! const Address rsp_after_call(rbp, rsp_after_call_off * wordSize); @@ -298,6 +308,7 @@ address StubGenerator::generate_call_stub(address& return_address) { BLOCK_COMMENT("call_stub_return_address:"); return_address = __ pc(); + entries.append(return_address); // store result depending on type (everything that is not // T_OBJECT, T_LONG, T_FLOAT or T_DOUBLE is treated as T_INT) @@ -394,6 +405,9 @@ address StubGenerator::generate_call_stub(address& return_address) { __ movdbl(Address(c_rarg0, 0), xmm0); __ jmp(exit); + // record the stub entry and end plus the auxiliary entry + store_archive_data(stub_id, start, __ pc(), &entries); + return start; } @@ -411,8 +425,15 @@ address StubGenerator::generate_call_stub(address& return_address) { address StubGenerator::generate_catch_exception() { StubId stub_id = StubId::stubgen_catch_exception_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); // same as in generate_call_stub(): const Address rsp_after_call(rbp, rsp_after_call_off * wordSize); @@ -442,7 +463,9 @@ address StubGenerator::generate_catch_exception() { __ verify_oop(rax); __ movptr(Address(r15_thread, Thread::pending_exception_offset()), rax); - __ lea(rscratch1, ExternalAddress((address)__FILE__)); + // special case -- add file name string to AOT address table + address file = (address)AOTCodeCache::add_C_string(__FILE__); + __ lea(rscratch1, ExternalAddress(file)); __ movptr(Address(r15_thread, Thread::exception_file_offset()), rscratch1); __ movl(Address(r15_thread, Thread::exception_line_offset()), (int) __LINE__); @@ -451,6 +474,9 @@ address StubGenerator::generate_catch_exception() { "_call_stub_return_address must have been generated before"); __ jump(RuntimeAddress(StubRoutines::_call_stub_return_address)); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -467,8 +493,14 @@ address StubGenerator::generate_catch_exception() { address StubGenerator::generate_forward_exception() { StubId stub_id = StubId::stubgen_forward_exception_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); // Upon entry, the sp points to the return address returning into // Java (interpreted or compiled) code; i.e., the return address @@ -521,6 +553,9 @@ address StubGenerator::generate_forward_exception() { __ verify_oop(rax); __ jmp(rbx); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -531,12 +566,21 @@ address StubGenerator::generate_forward_exception() { // Result: address StubGenerator::generate_orderaccess_fence() { StubId stub_id = StubId::stubgen_fence_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ membar(Assembler::StoreLoad); __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -550,8 +594,14 @@ address StubGenerator::generate_orderaccess_fence() { address StubGenerator::generate_verify_mxcsr() { StubId stub_id = StubId::stubgen_verify_mxcsr_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); const Address mxcsr_save(rsp, 0); @@ -574,15 +624,24 @@ address StubGenerator::generate_verify_mxcsr() { __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_f2i_fixup() { StubId stub_id = StubId::stubgen_f2i_fixup_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); Address inout(rsp, 5 * wordSize); // return address + 4 saves - address start = __ pc(); + start = __ pc(); Label L; @@ -613,14 +672,23 @@ address StubGenerator::generate_f2i_fixup() { __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_f2l_fixup() { StubId stub_id = StubId::stubgen_f2l_fixup_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); Address inout(rsp, 5 * wordSize); // return address + 4 saves - address start = __ pc(); + start = __ pc(); Label L; @@ -651,15 +719,24 @@ address StubGenerator::generate_f2l_fixup() { __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_d2i_fixup() { StubId stub_id = StubId::stubgen_d2i_fixup_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); Address inout(rsp, 6 * wordSize); // return address + 5 saves - address start = __ pc(); + start = __ pc(); Label L; @@ -699,15 +776,24 @@ address StubGenerator::generate_d2i_fixup() { __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_d2l_fixup() { StubId stub_id = StubId::stubgen_d2l_fixup_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); Address inout(rsp, 6 * wordSize); // return address + 5 saves - address start = __ pc(); + start = __ pc(); Label L; @@ -747,14 +833,23 @@ address StubGenerator::generate_d2l_fixup() { __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_count_leading_zeros_lut() { - __ align64(); StubId stub_id = StubId::stubgen_vector_count_leading_zeros_lut_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align64(); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ emit_data64(0x0101010102020304, relocInfo::none); __ emit_data64(0x0000000000000000, relocInfo::none); @@ -765,14 +860,23 @@ address StubGenerator::generate_count_leading_zeros_lut() { __ emit_data64(0x0101010102020304, relocInfo::none); __ emit_data64(0x0000000000000000, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_popcount_avx_lut() { - __ align64(); StubId stub_id = StubId::stubgen_vector_popcount_lut_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align64(); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ emit_data64(0x0302020102010100, relocInfo::none); __ emit_data64(0x0403030203020201, relocInfo::none); @@ -783,14 +887,30 @@ address StubGenerator::generate_popcount_avx_lut() { __ emit_data64(0x0302020102010100, relocInfo::none); __ emit_data64(0x0403030203020201, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } -address StubGenerator::generate_iota_indices() { - __ align(CodeEntryAlignment); +void StubGenerator::generate_iota_indices() { StubId stub_id = StubId::stubgen_vector_iota_indices_id; + GrowableArray
entries; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == VECTOR_IOTA_COUNT, "sanity check"); + address start = load_archive_data(stub_id, &entries); + if (start != nullptr) { + assert(entries.length() == VECTOR_IOTA_COUNT - 1, + "unexpected extra entry count %d", entries.length()); + StubRoutines::x86::_vector_iota_indices[0] = start; + for (int i = 1; i < VECTOR_IOTA_COUNT; i++) { + StubRoutines::x86::_vector_iota_indices[i] = entries.at(i - 1); + } + return; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); // B __ emit_data64(0x0706050403020100, relocInfo::none); __ emit_data64(0x0F0E0D0C0B0A0908, relocInfo::none); @@ -800,6 +920,7 @@ address StubGenerator::generate_iota_indices() { __ emit_data64(0x2F2E2D2C2B2A2928, relocInfo::none); __ emit_data64(0x3736353433323130, relocInfo::none); __ emit_data64(0x3F3E3D3C3B3A3938, relocInfo::none); + entries.append(__ pc()); // W __ emit_data64(0x0003000200010000, relocInfo::none); __ emit_data64(0x0007000600050004, relocInfo::none); @@ -809,6 +930,7 @@ address StubGenerator::generate_iota_indices() { __ emit_data64(0x0017001600150014, relocInfo::none); __ emit_data64(0x001B001A00190018, relocInfo::none); __ emit_data64(0x001F001E001D001C, relocInfo::none); + entries.append(__ pc()); // D __ emit_data64(0x0000000100000000, relocInfo::none); __ emit_data64(0x0000000300000002, relocInfo::none); @@ -818,6 +940,7 @@ address StubGenerator::generate_iota_indices() { __ emit_data64(0x0000000B0000000A, relocInfo::none); __ emit_data64(0x0000000D0000000C, relocInfo::none); __ emit_data64(0x0000000F0000000E, relocInfo::none); + entries.append(__ pc()); // Q __ emit_data64(0x0000000000000000, relocInfo::none); __ emit_data64(0x0000000000000001, relocInfo::none); @@ -827,6 +950,7 @@ address StubGenerator::generate_iota_indices() { __ emit_data64(0x0000000000000005, relocInfo::none); __ emit_data64(0x0000000000000006, relocInfo::none); __ emit_data64(0x0000000000000007, relocInfo::none); + entries.append(__ pc()); // D - FP __ emit_data64(0x3F80000000000000, relocInfo::none); // 0.0f, 1.0f __ emit_data64(0x4040000040000000, relocInfo::none); // 2.0f, 3.0f @@ -836,6 +960,7 @@ address StubGenerator::generate_iota_indices() { __ emit_data64(0x4130000041200000, relocInfo::none); // 10.0f, 11.0f __ emit_data64(0x4150000041400000, relocInfo::none); // 12.0f, 13.0f __ emit_data64(0x4170000041600000, relocInfo::none); // 14.0f, 15.0f + entries.append(__ pc()); // Q - FP __ emit_data64(0x0000000000000000, relocInfo::none); // 0.0d __ emit_data64(0x3FF0000000000000, relocInfo::none); // 1.0d @@ -845,14 +970,30 @@ address StubGenerator::generate_iota_indices() { __ emit_data64(0x4014000000000000, relocInfo::none); // 5.0d __ emit_data64(0x4018000000000000, relocInfo::none); // 6.0d __ emit_data64(0x401c000000000000, relocInfo::none); // 7.0d - return start; + + // record the stub entry and end + store_archive_data(stub_id, start, __ pc(), &entries); + + // install the entry addresses in the entry array + assert(entries.length() == entry_count - 1, + "unexpected entries count %d", entries.length()); + StubRoutines::x86::_vector_iota_indices[0] = start; + for (int i = 1; i < VECTOR_IOTA_COUNT; i++) { + StubRoutines::x86::_vector_iota_indices[i] = entries.at(i - 1); + } } address StubGenerator::generate_vector_reverse_bit_lut() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_vector_reverse_bit_lut_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ emit_data64(0x0E060A020C040800, relocInfo::none); __ emit_data64(0x0F070B030D050901, relocInfo::none); @@ -863,14 +1004,23 @@ address StubGenerator::generate_vector_reverse_bit_lut() { __ emit_data64(0x0E060A020C040800, relocInfo::none); __ emit_data64(0x0F070B030D050901, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_vector_reverse_byte_perm_mask_long() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_vector_reverse_byte_perm_mask_long_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ emit_data64(0x0001020304050607, relocInfo::none); __ emit_data64(0x08090A0B0C0D0E0F, relocInfo::none); @@ -881,14 +1031,23 @@ address StubGenerator::generate_vector_reverse_byte_perm_mask_long() { __ emit_data64(0x0001020304050607, relocInfo::none); __ emit_data64(0x08090A0B0C0D0E0F, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_vector_reverse_byte_perm_mask_int() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_vector_reverse_byte_perm_mask_int_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ emit_data64(0x0405060700010203, relocInfo::none); __ emit_data64(0x0C0D0E0F08090A0B, relocInfo::none); @@ -899,14 +1058,23 @@ address StubGenerator::generate_vector_reverse_byte_perm_mask_int() { __ emit_data64(0x0405060700010203, relocInfo::none); __ emit_data64(0x0C0D0E0F08090A0B, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_vector_reverse_byte_perm_mask_short() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_vector_reverse_byte_perm_mask_short_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ emit_data64(0x0607040502030001, relocInfo::none); __ emit_data64(0x0E0F0C0D0A0B0809, relocInfo::none); @@ -917,31 +1085,52 @@ address StubGenerator::generate_vector_reverse_byte_perm_mask_short() { __ emit_data64(0x0607040502030001, relocInfo::none); __ emit_data64(0x0E0F0C0D0A0B0809, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_vector_byte_shuffle_mask() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_vector_byte_shuffle_mask_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ emit_data64(0x7070707070707070, relocInfo::none); __ emit_data64(0x7070707070707070, relocInfo::none); __ emit_data64(0xF0F0F0F0F0F0F0F0, relocInfo::none); __ emit_data64(0xF0F0F0F0F0F0F0F0, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_fp_mask(StubId stub_id, int64_t mask) { + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ emit_data64( mask, relocInfo::none ); __ emit_data64( mask, relocInfo::none ); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -957,9 +1146,15 @@ address StubGenerator::generate_compress_perm_table(StubId stub_id) { default: ShouldNotReachHere(); } + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); if (esize == 32) { // Loop to generate 256 x 8 int compression permute index table. A row is // accessed using 8 bit index computed using vector mask. An entry in @@ -997,6 +1192,9 @@ address StubGenerator::generate_compress_perm_table(StubId stub_id) { } } } + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -1012,9 +1210,15 @@ address StubGenerator::generate_expand_perm_table(StubId stub_id) { default: ShouldNotReachHere(); } + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); if (esize == 32) { // Loop to generate 256 x 8 int expand permute index table. A row is accessed // using 8 bit index computed using vector mask. An entry in a row holds either @@ -1050,13 +1254,22 @@ address StubGenerator::generate_expand_perm_table(StubId stub_id) { } } } + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_vector_mask(StubId stub_id, int64_t mask) { + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ emit_data64(mask, relocInfo::none); __ emit_data64(mask, relocInfo::none); @@ -1067,14 +1280,23 @@ address StubGenerator::generate_vector_mask(StubId stub_id, int64_t mask) { __ emit_data64(mask, relocInfo::none); __ emit_data64(mask, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_vector_byte_perm_mask() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_vector_byte_perm_mask_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ emit_data64(0x0000000000000001, relocInfo::none); __ emit_data64(0x0000000000000003, relocInfo::none); @@ -1085,13 +1307,22 @@ address StubGenerator::generate_vector_byte_perm_mask() { __ emit_data64(0x0000000000000004, relocInfo::none); __ emit_data64(0x0000000000000006, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_vector_fp_mask(StubId stub_id, int64_t mask) { + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ emit_data64(mask, relocInfo::none); __ emit_data64(mask, relocInfo::none); @@ -1102,6 +1333,9 @@ address StubGenerator::generate_vector_fp_mask(StubId stub_id, int64_t mask) { __ emit_data64(mask, relocInfo::none); __ emit_data64(mask, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -1110,9 +1344,15 @@ address StubGenerator::generate_vector_custom_i32(StubId stub_id, Assembler::Avx int32_t val4, int32_t val5, int32_t val6, int32_t val7, int32_t val8, int32_t val9, int32_t val10, int32_t val11, int32_t val12, int32_t val13, int32_t val14, int32_t val15) { + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); assert(len != Assembler::AVX_NoVec, "vector len must be specified"); __ emit_data(val0, relocInfo::none, 0); @@ -1135,6 +1375,9 @@ address StubGenerator::generate_vector_custom_i32(StubId stub_id, Assembler::Avx __ emit_data(val15, relocInfo::none, 0); } } + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -1156,8 +1399,14 @@ address StubGenerator::generate_vector_custom_i32(StubId stub_id, Assembler::Avx // * = popped on exit address StubGenerator::generate_verify_oop() { StubId stub_id = StubId::stubgen_verify_oop_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label exit, error; @@ -1235,6 +1484,9 @@ address StubGenerator::generate_verify_oop() { __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, MacroAssembler::debug64))); __ hlt(); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -1350,35 +1602,46 @@ void StubGenerator::restore_argument_regs(BasicType type) { address StubGenerator::generate_data_cache_writeback() { const Register src = c_rarg0; // source address - - __ align(CodeEntryAlignment); - StubId stub_id = StubId::stubgen_data_cache_writeback_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); __ cache_wb(Address(src, 0)); __ leave(); __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_data_cache_writeback_sync() { const Register is_pre = c_rarg0; // pre or post sync - - __ align(CodeEntryAlignment); - StubId stub_id = StubId::stubgen_data_cache_writeback_sync_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); // pre wbsync is a no-op // post wbsync translates to an sfence Label skip; - address start = __ pc(); + start = __ pc(); __ enter(); __ cmpl(is_pre, 0); @@ -1388,6 +1651,9 @@ address StubGenerator::generate_data_cache_writeback_sync() { __ leave(); __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -1405,9 +1671,15 @@ address StubGenerator::generate_md5_implCompress(StubId stub_id) { default: ShouldNotReachHere(); } + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); const Register buf_param = r15; const Address state_param(rsp, 0 * wordSize); @@ -1437,30 +1709,51 @@ address StubGenerator::generate_md5_implCompress(StubId stub_id) { __ leave(); __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_upper_word_mask() { - __ align64(); StubId stub_id = StubId::stubgen_upper_word_mask_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align64(); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ emit_data64(0x0000000000000000, relocInfo::none); __ emit_data64(0xFFFFFFFF00000000, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_shuffle_byte_flip_mask() { - __ align64(); StubId stub_id = StubId::stubgen_shuffle_byte_flip_mask_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align64(); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ emit_data64(0x08090a0b0c0d0e0f, relocInfo::none); __ emit_data64(0x0001020304050607, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -1478,9 +1771,15 @@ address StubGenerator::generate_sha1_implCompress(StubId stub_id) { default: ShouldNotReachHere(); } + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Register buf = c_rarg0; Register state = c_rarg1; @@ -1509,15 +1808,32 @@ address StubGenerator::generate_sha1_implCompress(StubId stub_id) { __ leave(); __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } -address StubGenerator::generate_pshuffle_byte_flip_mask() { - __ align64(); +address StubGenerator::generate_pshuffle_byte_flip_mask(address& entry_00ba, address& entry_dc00) { StubId stub_id = StubId::stubgen_pshuffle_byte_flip_mask_id; + GrowableArray
entries; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 3, "sanity check"); + address start = load_archive_data(stub_id, &entries); + if (start != nullptr) { + assert(entries.length() == entry_count - 1, + "unexpected extra entry count %d", entries.length()); + entry_00ba = entries.at(0); + entry_dc00 = entries.at(1); + assert(VM_Version::supports_avx2() == (entry_00ba != nullptr && entry_dc00 != nullptr), + "entries cannot be null when avx2 is enabled"); + return start; + } + __ align64(); StubCodeMark mark(this, stub_id); - address start = __ pc(); - + start = __ pc(); + address entry2 = nullptr; + address entry3 = nullptr; __ emit_data64(0x0405060700010203, relocInfo::none); __ emit_data64(0x0c0d0e0f08090a0b, relocInfo::none); @@ -1525,37 +1841,66 @@ address StubGenerator::generate_pshuffle_byte_flip_mask() { __ emit_data64(0x0405060700010203, relocInfo::none); // second copy __ emit_data64(0x0c0d0e0f08090a0b, relocInfo::none); // _SHUF_00BA + entry2 = __ pc(); __ emit_data64(0x0b0a090803020100, relocInfo::none); __ emit_data64(0xFFFFFFFFFFFFFFFF, relocInfo::none); __ emit_data64(0x0b0a090803020100, relocInfo::none); __ emit_data64(0xFFFFFFFFFFFFFFFF, relocInfo::none); // _SHUF_DC00 + entry3 = __ pc(); __ emit_data64(0xFFFFFFFFFFFFFFFF, relocInfo::none); __ emit_data64(0x0b0a090803020100, relocInfo::none); __ emit_data64(0xFFFFFFFFFFFFFFFF, relocInfo::none); __ emit_data64(0x0b0a090803020100, relocInfo::none); } + // have to track the 2nd and 3rd entries even if they are null + entry_00ba = entry2; + entries.push(entry_00ba); + entry_dc00 = entry3; + entries.push(entry_dc00); + + // record the stub entry and end plus all the auxiliary entries + store_archive_data(stub_id, start, __ pc(), &entries); return start; } //Mask for byte-swapping a couple of qwords in an XMM register using (v)pshufb. -address StubGenerator::generate_pshuffle_byte_flip_mask_sha512() { - __ align32(); +address StubGenerator::generate_pshuffle_byte_flip_mask_sha512(address& entry_ymm_lo) { StubId stub_id = StubId::stubgen_pshuffle_byte_flip_mask_sha512_id; + GrowableArray
entries; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 2, "sanity check"); + address start = load_archive_data(stub_id, &entries); + if (start != nullptr) { + assert(entries.length() == entry_count - 1, + "unexpected extra entry count %d", entries.length()); + entry_ymm_lo = entries.at(0); + assert(VM_Version::supports_avx2() == (entry_ymm_lo != nullptr), + "entry cannot be null when avx2 is enabled"); + return start; + } + __ align32(); StubCodeMark mark(this, stub_id); - address start = __ pc(); - + start = __ pc(); + address entry2 = nullptr; if (VM_Version::supports_avx2()) { __ emit_data64(0x0001020304050607, relocInfo::none); // PSHUFFLE_BYTE_FLIP_MASK __ emit_data64(0x08090a0b0c0d0e0f, relocInfo::none); __ emit_data64(0x1011121314151617, relocInfo::none); __ emit_data64(0x18191a1b1c1d1e1f, relocInfo::none); + // capture 2nd entry + entry2 = __ pc(); __ emit_data64(0x0000000000000000, relocInfo::none); //MASK_YMM_LO __ emit_data64(0x0000000000000000, relocInfo::none); __ emit_data64(0xFFFFFFFFFFFFFFFF, relocInfo::none); __ emit_data64(0xFFFFFFFFFFFFFFFF, relocInfo::none); } + // have to track the 2nd entry even if it is null + entry_ymm_lo = entry2; + entries.push(entry2); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc(), &entries); return start; } @@ -1575,9 +1920,15 @@ address StubGenerator::generate_sha256_implCompress(StubId stub_id) { ShouldNotReachHere(); } assert(VM_Version::supports_sha() || VM_Version::supports_avx2(), ""); + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Register buf = c_rarg0; Register state = c_rarg1; @@ -1612,6 +1963,9 @@ address StubGenerator::generate_sha256_implCompress(StubId stub_id) { __ leave(); __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -1629,9 +1983,15 @@ address StubGenerator::generate_sha512_implCompress(StubId stub_id) { } assert(VM_Version::supports_avx2(), ""); assert(VM_Version::supports_bmi2() || VM_Version::supports_sha512(), ""); + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Register buf = c_rarg0; Register state = c_rarg1; @@ -1660,14 +2020,23 @@ address StubGenerator::generate_sha512_implCompress(StubId stub_id) { __ leave(); __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::base64_shuffle_addr() { - __ align64(); StubId stub_id = StubId::stubgen_shuffle_base64_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align64(); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); assert(((unsigned long long)start & 0x3f) == 0, "Alignment problem (0x%08llx)", (unsigned long long)start); @@ -1680,42 +2049,69 @@ address StubGenerator::base64_shuffle_addr() { __ emit_data64(0x2829272825262425, relocInfo::none); __ emit_data64(0x2e2f2d2e2b2c2a2b, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::base64_avx2_shuffle_addr() { - __ align32(); StubId stub_id = StubId::stubgen_avx2_shuffle_base64_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align32(); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ emit_data64(0x0809070805060405, relocInfo::none); __ emit_data64(0x0e0f0d0e0b0c0a0b, relocInfo::none); __ emit_data64(0x0405030401020001, relocInfo::none); __ emit_data64(0x0a0b090a07080607, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::base64_avx2_input_mask_addr() { - __ align32(); StubId stub_id = StubId::stubgen_avx2_input_mask_base64_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align32(); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ emit_data64(0x8000000000000000, relocInfo::none); __ emit_data64(0x8000000080000000, relocInfo::none); __ emit_data64(0x8000000080000000, relocInfo::none); __ emit_data64(0x8000000080000000, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::base64_avx2_lut_addr() { - __ align32(); StubId stub_id = StubId::stubgen_avx2_lut_base64_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align32(); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ emit_data64(0xfcfcfcfcfcfc4741, relocInfo::none); __ emit_data64(0x0000f0edfcfcfcfc, relocInfo::none); @@ -1728,14 +2124,23 @@ address StubGenerator::base64_avx2_lut_addr() { __ emit_data64(0xfcfcfcfcfcfc4741, relocInfo::none); __ emit_data64(0x000020effcfcfcfc, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::base64_encoding_table_addr() { - __ align64(); StubId stub_id = StubId::stubgen_encoding_table_base64_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align64(); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); assert(((unsigned long long)start & 0x3f) == 0, "Alignment problem (0x%08llx)", (unsigned long long)start); __ emit_data64(0x4847464544434241, relocInfo::none); @@ -1757,6 +2162,9 @@ address StubGenerator::base64_encoding_table_addr() { __ emit_data64(0x333231307a797877, relocInfo::none); __ emit_data64(0x5f2d393837363534, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -1766,10 +2174,16 @@ address StubGenerator::base64_encoding_table_addr() { // boolean isURL) { address StubGenerator::generate_base64_encodeBlock() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_base64_encodeBlock_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); @@ -2144,15 +2558,24 @@ address StubGenerator::generate_base64_encodeBlock() __ leave(); __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } // base64 AVX512vbmi tables address StubGenerator::base64_vbmi_lookup_lo_addr() { - __ align64(); StubId stub_id = StubId::stubgen_lookup_lo_base64_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align64(); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); assert(((unsigned long long)start & 0x3f) == 0, "Alignment problem (0x%08llx)", (unsigned long long)start); @@ -2165,14 +2588,23 @@ address StubGenerator::base64_vbmi_lookup_lo_addr() { __ emit_data64(0x3b3a393837363534, relocInfo::none); __ emit_data64(0x8080808080803d3c, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::base64_vbmi_lookup_hi_addr() { - __ align64(); StubId stub_id = StubId::stubgen_lookup_hi_base64_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align64(); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); assert(((unsigned long long)start & 0x3f) == 0, "Alignment problem (0x%08llx)", (unsigned long long)start); @@ -2185,13 +2617,22 @@ address StubGenerator::base64_vbmi_lookup_hi_addr() { __ emit_data64(0x302f2e2d2c2b2a29, relocInfo::none); __ emit_data64(0x8080808080333231, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::base64_vbmi_lookup_lo_url_addr() { - __ align64(); StubId stub_id = StubId::stubgen_lookup_lo_base64url_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align64(); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); assert(((unsigned long long)start & 0x3f) == 0, "Alignment problem (0x%08llx)", (unsigned long long)start); @@ -2204,14 +2645,23 @@ address StubGenerator::base64_vbmi_lookup_lo_url_addr() { __ emit_data64(0x3b3a393837363534, relocInfo::none); __ emit_data64(0x8080808080803d3c, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::base64_vbmi_lookup_hi_url_addr() { - __ align64(); StubId stub_id = StubId::stubgen_lookup_hi_base64url_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align64(); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); assert(((unsigned long long)start & 0x3f) == 0, "Alignment problem (0x%08llx)", (unsigned long long)start); @@ -2224,14 +2674,23 @@ address StubGenerator::base64_vbmi_lookup_hi_url_addr() { __ emit_data64(0x302f2e2d2c2b2a29, relocInfo::none); __ emit_data64(0x8080808080333231, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::base64_vbmi_pack_vec_addr() { - __ align64(); StubId stub_id = StubId::stubgen_pack_vec_base64_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align64(); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); assert(((unsigned long long)start & 0x3f) == 0, "Alignment problem (0x%08llx)", (unsigned long long)start); @@ -2244,14 +2703,23 @@ address StubGenerator::base64_vbmi_pack_vec_addr() { __ emit_data64(0x0000000000000000, relocInfo::none); __ emit_data64(0x0000000000000000, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::base64_vbmi_join_0_1_addr() { - __ align64(); StubId stub_id = StubId::stubgen_join_0_1_base64_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align64(); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); assert(((unsigned long long)start & 0x3f) == 0, "Alignment problem (0x%08llx)", (unsigned long long)start); @@ -2264,14 +2732,23 @@ address StubGenerator::base64_vbmi_join_0_1_addr() { __ emit_data64(0x494a444546404142, relocInfo::none); __ emit_data64(0x565051524c4d4e48, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::base64_vbmi_join_1_2_addr() { - __ align64(); StubId stub_id = StubId::stubgen_join_1_2_base64_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align64(); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); assert(((unsigned long long)start & 0x3f) == 0, "Alignment problem (0x%08llx)", (unsigned long long)start); @@ -2284,14 +2761,23 @@ address StubGenerator::base64_vbmi_join_1_2_addr() { __ emit_data64(0x5c5d5e58595a5455, relocInfo::none); __ emit_data64(0x696a646566606162, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::base64_vbmi_join_2_3_addr() { - __ align64(); StubId stub_id = StubId::stubgen_join_2_3_base64_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align64(); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); assert(((unsigned long long)start & 0x3f) == 0, "Alignment problem (0x%08llx)", (unsigned long long)start); @@ -2304,14 +2790,23 @@ address StubGenerator::base64_vbmi_join_2_3_addr() { __ emit_data64(0x767071726c6d6e68, relocInfo::none); __ emit_data64(0x7c7d7e78797a7475, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::base64_AVX2_decode_tables_addr() { - __ align64(); StubId stub_id = StubId::stubgen_avx2_decode_tables_base64_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align64(); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); assert(((unsigned long long)start & 0x3f) == 0, "Alignment problem (0x%08llx)", (unsigned long long)start); @@ -2339,14 +2834,23 @@ address StubGenerator::base64_AVX2_decode_tables_addr() { // merge multiplier __ emit_data(0x00011000, relocInfo::none, 0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::base64_AVX2_decode_LUT_tables_addr() { - __ align64(); StubId stub_id = StubId::stubgen_avx2_decode_lut_tables_base64_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align64(); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); assert(((unsigned long long)start & 0x3f) == 0, "Alignment problem (0x%08llx)", (unsigned long long)start); @@ -2380,13 +2884,22 @@ address StubGenerator::base64_AVX2_decode_LUT_tables_addr() { __ emit_data64(0x0804080402011010, relocInfo::none); __ emit_data64(0x1010101010101010, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::base64_decoding_table_addr() { StubId stub_id = StubId::stubgen_decoding_table_base64_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ emit_data64(0xffffffffffffffff, relocInfo::none); __ emit_data64(0xffffffffffffffff, relocInfo::none); @@ -2455,6 +2968,9 @@ address StubGenerator::base64_decoding_table_addr() { __ emit_data64(0xffffffffffffffff, relocInfo::none); __ emit_data64(0xffffffffffffffff, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -2466,10 +2982,16 @@ address StubGenerator::base64_decoding_table_addr() { // Intrinsic function prototype in Base64.java: // private void decodeBlock(byte[] src, int sp, int sl, byte[] dst, int dp, boolean isURL, isMIME) { address StubGenerator::generate_base64_decodeBlock() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_base64_decodeBlock_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); @@ -2982,6 +3504,9 @@ address StubGenerator::generate_base64_decodeBlock() { __ leave(); __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -3000,11 +3525,17 @@ address StubGenerator::generate_base64_decodeBlock() { address StubGenerator::generate_updateBytesCRC32() { assert(UseCRC32Intrinsics, "need AVX and CLMUL instructions"); - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_updateBytesCRC32_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); // Win64: rcx, rdx, r8, r9 (c_rarg0, c_rarg1, ...) // Unix: rdi, rsi, rdx, rcx, r8, r9 (c_rarg0, c_rarg1, ...) @@ -3039,6 +3570,9 @@ address StubGenerator::generate_updateBytesCRC32() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -3057,10 +3591,16 @@ address StubGenerator::generate_updateBytesCRC32() { */ address StubGenerator::generate_updateBytesCRC32C(bool is_pclmulqdq_supported) { assert(UseCRC32CIntrinsics, "need SSE4_2"); - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_updateBytesCRC32C_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); //reg.arg int#0 int#1 int#2 int#3 int#4 int#5 float regs //Windows RCX RDX R8 R9 none none XMM0..XMM3 @@ -3120,6 +3660,9 @@ address StubGenerator::generate_updateBytesCRC32C(bool is_pclmulqdq_supported) { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -3138,10 +3681,16 @@ address StubGenerator::generate_updateBytesCRC32C(bool is_pclmulqdq_supported) { * rsp+40 - z address */ address StubGenerator::generate_multiplyToLen() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_multiplyToLen_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); // Win64: rcx, rdx, r8, r9 (c_rarg0, c_rarg1, ...) // Unix: rdi, rsi, rdx, rcx, r8, r9 (c_rarg0, c_rarg1, ...) @@ -3179,6 +3728,9 @@ address StubGenerator::generate_multiplyToLen() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -3195,10 +3747,16 @@ address StubGenerator::generate_multiplyToLen() { * rax - int >= mismatched index, < 0 bitwise complement of tail */ address StubGenerator::generate_vectorizedMismatch() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_vectorizedMismatch_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); BLOCK_COMMENT("Entry:"); __ enter(); @@ -3232,6 +3790,9 @@ address StubGenerator::generate_vectorizedMismatch() { __ leave(); __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -3247,10 +3808,16 @@ address StubGenerator::generate_vectorizedMismatch() { */ address StubGenerator::generate_squareToLen() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_squareToLen_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); // Win64: rcx, rdx, r8, r9 (c_rarg0, c_rarg1, ...) // Unix: rdi, rsi, rdx, rcx (c_rarg0, c_rarg1, ...) @@ -3279,14 +3846,23 @@ address StubGenerator::generate_squareToLen() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_method_entry_barrier() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_method_entry_barrier_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label deoptimize_label; @@ -3356,6 +3932,9 @@ address StubGenerator::generate_method_entry_barrier() { __ movptr(rsp, Address(rsp, 0)); // new rsp was written in the barrier __ jmp(Address(rsp, -1 * wordSize)); // jmp target should be callers verified_entry_point + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -3373,10 +3952,16 @@ address StubGenerator::generate_method_entry_barrier() { * rsp+40 - k */ address StubGenerator::generate_mulAdd() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_mulAdd_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); // Win64: rcx, rdx, r8, r9 (c_rarg0, c_rarg1, ...) // Unix: rdi, rsi, rdx, rcx, r8, r9 (c_rarg0, c_rarg1, ...) @@ -3411,14 +3996,23 @@ address StubGenerator::generate_mulAdd() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_bigIntegerRightShift() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_bigIntegerRightShiftWorker_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label Shift512Loop, ShiftTwo, ShiftTwoLoop, ShiftOne, Exit; // For Unix, the arguments are as follows: rdi, rsi, rdx, rcx, r8. @@ -3534,6 +4128,9 @@ address StubGenerator::generate_bigIntegerRightShift() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -3551,10 +4148,16 @@ address StubGenerator::generate_bigIntegerRightShift() { * rsp40 - numIter */ address StubGenerator::generate_bigIntegerLeftShift() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_bigIntegerLeftShiftWorker_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label Shift512Loop, ShiftTwo, ShiftTwoLoop, ShiftOne, Exit; // For Unix, the arguments are as follows: rdi, rsi, rdx, rcx, r8. @@ -3659,6 +4262,9 @@ address StubGenerator::generate_bigIntegerLeftShift() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -3708,9 +4314,15 @@ void StubGenerator::generate_libm_stubs() { */ address StubGenerator::generate_float16ToFloat() { StubId stub_id = StubId::stubgen_hf2f_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); BLOCK_COMMENT("Entry:"); // No need for RuntimeStub frame since it is called only during JIT compilation @@ -3720,6 +4332,9 @@ address StubGenerator::generate_float16ToFloat() { __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -3734,9 +4349,15 @@ address StubGenerator::generate_float16ToFloat() { */ address StubGenerator::generate_floatToFloat16() { StubId stub_id = StubId::stubgen_f2hf_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); BLOCK_COMMENT("Entry:"); // No need for RuntimeStub frame since it is called only during JIT compilation @@ -3746,6 +4367,9 @@ address StubGenerator::generate_floatToFloat16() { __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -3775,8 +4399,14 @@ address StubGenerator::generate_cont_thaw(StubId stub_id) { default: ShouldNotReachHere(); } + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); // TODO: Handle Valhalla return types. May require generating different return barriers. @@ -3889,6 +4519,9 @@ address StubGenerator::generate_cont_thaw(StubId stub_id) { __ ret(0); } + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -3909,8 +4542,14 @@ address StubGenerator::generate_cont_returnBarrier_exception() { address StubGenerator::generate_cont_preempt_stub() { if (!Continuations::enabled()) return nullptr; StubId stub_id = StubId::stubgen_cont_preempt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ reset_last_Java_frame(true); @@ -3934,14 +4573,23 @@ address StubGenerator::generate_cont_preempt_stub() { __ movptr(rscratch1, ExternalAddress(ContinuationEntry::thaw_call_pc_address())); __ jmp(rscratch1); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } // exception handler for upcall stubs address StubGenerator::generate_upcall_stub_exception_handler() { StubId stub_id = StubId::stubgen_upcall_stub_exception_handler_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); // native caller has no idea how to handle exceptions // we just crash here. Up to callee to catch exceptions. @@ -3953,6 +4601,9 @@ address StubGenerator::generate_upcall_stub_exception_handler() { __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, UpcallLinker::handle_uncaught_exception))); __ should_not_reach_here(); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -3961,8 +4612,14 @@ address StubGenerator::generate_upcall_stub_exception_handler() { // rbx = result address StubGenerator::generate_upcall_stub_load_target() { StubId stub_id = StubId::stubgen_upcall_stub_load_target_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ resolve_global_jobject(j_rarg0, rscratch1); // Load target method from receiver @@ -3976,11 +4633,27 @@ address StubGenerator::generate_upcall_stub_load_target() { __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } void StubGenerator::generate_lookup_secondary_supers_table_stub() { StubId stub_id = StubId::stubgen_lookup_secondary_supers_table_id; + GrowableArray
entries; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == Klass::SECONDARY_SUPERS_TABLE_SIZE, "sanity check"); + address start = load_archive_data(stub_id, &entries); + if (start != nullptr) { + assert(entries.length() == Klass::SECONDARY_SUPERS_TABLE_SIZE - 1, + "unexpected extra entry count %d", entries.length()); + StubRoutines::_lookup_secondary_supers_table_stubs[0] = start; + for (int slot = 1; slot < Klass::SECONDARY_SUPERS_TABLE_SIZE; slot++) { + StubRoutines::_lookup_secondary_supers_table_stubs[slot] = entries.at(slot - 1); + } + return; + } StubCodeMark mark(this, stub_id); const Register @@ -3989,21 +4662,35 @@ void StubGenerator::generate_lookup_secondary_supers_table_stub() { result = rdi; for (int slot = 0; slot < Klass::SECONDARY_SUPERS_TABLE_SIZE; slot++) { - StubRoutines::_lookup_secondary_supers_table_stubs[slot] = __ pc(); + address next_entry = __ pc(); + if (slot == 0) { + start = next_entry; + } else { + entries.append(next_entry); + } + StubRoutines::_lookup_secondary_supers_table_stubs[slot] = next_entry; __ lookup_secondary_supers_table_const(r_sub_klass, r_super_klass, rdx, rcx, rbx, r11, // temps result, slot); __ ret(0); } + + // record the stub entry and end plus all the auxiliary entries + store_archive_data(stub_id, start, __ pc(), &entries); } // Slow path implementation for UseSecondarySupersTable. address StubGenerator::generate_lookup_secondary_supers_table_slow_path_stub() { StubId stub_id = StubId::stubgen_lookup_secondary_supers_table_slow_path_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - - address start = __ pc(); + start = __ pc(); const Register r_super_klass = rax, @@ -4025,6 +4712,9 @@ address StubGenerator::generate_lookup_secondary_supers_table_slow_path_stub() { __ movl(result, 0); __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -4165,7 +4855,7 @@ void StubGenerator::generate_compiler_stubs() { StubRoutines::x86::_vector_short_shuffle_mask = generate_vector_mask(StubId::stubgen_vector_short_shuffle_mask_id, 0x0100010001000100); StubRoutines::x86::_vector_long_shuffle_mask = generate_vector_mask(StubId::stubgen_vector_long_shuffle_mask_id, 0x0000000100000000); StubRoutines::x86::_vector_long_sign_mask = generate_vector_mask(StubId::stubgen_vector_long_sign_mask_id, 0x8000000000000000); - StubRoutines::x86::_vector_iota_indices = generate_iota_indices(); + generate_iota_indices(); StubRoutines::x86::_vector_count_leading_zeros_lut = generate_count_leading_zeros_lut(); StubRoutines::x86::_vector_reverse_bit_lut = generate_vector_reverse_bit_lut(); StubRoutines::x86::_vector_reverse_byte_perm_mask_long = generate_vector_reverse_byte_perm_mask_long(); @@ -4232,6 +4922,8 @@ void StubGenerator::generate_compiler_stubs() { } if (UseSHA256Intrinsics) { + address entry2 = nullptr; + address entry3 = nullptr; StubRoutines::x86::_k256_adr = (address)StubRoutines::x86::_k256; char* dst = (char*)StubRoutines::x86::_k256_W; char* src = (char*)StubRoutines::x86::_k256; @@ -4240,14 +4932,18 @@ void StubGenerator::generate_compiler_stubs() { memcpy(dst + 32 * ii + 16, src + 16 * ii, 16); } StubRoutines::x86::_k256_W_adr = (address)StubRoutines::x86::_k256_W; - StubRoutines::x86::_pshuffle_byte_flip_mask_addr = generate_pshuffle_byte_flip_mask(); + StubRoutines::x86::_pshuffle_byte_flip_mask_addr = generate_pshuffle_byte_flip_mask(entry2, entry3); + StubRoutines::x86::_pshuffle_byte_flip_mask_00ba_addr = entry2; + StubRoutines::x86::_pshuffle_byte_flip_mask_dc00_addr = entry3; StubRoutines::_sha256_implCompress = generate_sha256_implCompress(StubId::stubgen_sha256_implCompress_id); StubRoutines::_sha256_implCompressMB = generate_sha256_implCompress(StubId::stubgen_sha256_implCompressMB_id); } if (UseSHA512Intrinsics) { + address entry2 = nullptr; StubRoutines::x86::_k512_W_addr = (address)StubRoutines::x86::_k512_W; - StubRoutines::x86::_pshuffle_byte_flip_mask_addr_sha512 = generate_pshuffle_byte_flip_mask_sha512(); + StubRoutines::x86::_pshuffle_byte_flip_mask_addr_sha512 = generate_pshuffle_byte_flip_mask_sha512(entry2); + StubRoutines::x86::_pshuffle_byte_flip_mask_ymm_lo_addr_sha512 = entry2; StubRoutines::_sha512_implCompress = generate_sha512_implCompress(StubId::stubgen_sha512_implCompress_id); StubRoutines::_sha512_implCompressMB = generate_sha512_implCompress(StubId::stubgen_sha512_implCompressMB_id); } @@ -4325,7 +5021,7 @@ void StubGenerator::generate_compiler_stubs() { #endif // COMPILER2_OR_JVMCI } -StubGenerator::StubGenerator(CodeBuffer* code, BlobId blob_id) : StubCodeGenerator(code, blob_id) { +StubGenerator::StubGenerator(CodeBuffer* code, BlobId blob_id, AOTStubData* stub_data) : StubCodeGenerator(code, blob_id, stub_data) { switch(blob_id) { case BlobId::stubgen_preuniverse_id: generate_preuniverse_stubs(); @@ -4348,8 +5044,35 @@ StubGenerator::StubGenerator(CodeBuffer* code, BlobId blob_id) : StubCodeGenerat }; } -void StubGenerator_generate(CodeBuffer* code, BlobId blob_id) { - StubGenerator g(code, blob_id); +#if INCLUDE_CDS +// publish addresses of static data defined in this file and in other +// stubgen stub generator files +void StubGenerator::init_AOTAddressTable(GrowableArray
& external_addresses) { + init_AOTAddressTable_adler(external_addresses); + init_AOTAddressTable_aes(external_addresses); + init_AOTAddressTable_cbrt(external_addresses); + init_AOTAddressTable_chacha(external_addresses); + // constants publishes for all of address use by cos and almost all of sin + init_AOTAddressTable_constants(external_addresses); + init_AOTAddressTable_dilithium(external_addresses); + init_AOTAddressTable_exp(external_addresses); + init_AOTAddressTable_fmod(external_addresses); + init_AOTAddressTable_ghash(external_addresses); + init_AOTAddressTable_kyber(external_addresses); + init_AOTAddressTable_log(external_addresses); + init_AOTAddressTable_poly1305(external_addresses); + init_AOTAddressTable_poly_mont(external_addresses); + init_AOTAddressTable_pow(external_addresses); + init_AOTAddressTable_sha3(external_addresses); + init_AOTAddressTable_sin(external_addresses); + init_AOTAddressTable_sinh(external_addresses); + init_AOTAddressTable_tan(external_addresses); + init_AOTAddressTable_tanh(external_addresses); +} +#endif // INCLUDE_CDS + +void StubGenerator_generate(CodeBuffer* code, BlobId blob_id, AOTStubData* stub_data) { + StubGenerator g(code, blob_id, stub_data); } #undef __ diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp index 332add6dcd4..d3823cb559f 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp @@ -84,7 +84,7 @@ class StubGenerator: public StubCodeGenerator { address generate_count_leading_zeros_lut(); address generate_popcount_avx_lut(); - address generate_iota_indices(); + void generate_iota_indices(); address generate_vector_reverse_bit_lut(); address generate_vector_reverse_byte_perm_mask_long(); @@ -303,11 +303,11 @@ class StubGenerator: public StubCodeGenerator { address generate_sha512_implCompress(StubId stub_id); // Mask for byte-swapping a couple of qwords in an XMM register using (v)pshufb. - address generate_pshuffle_byte_flip_mask_sha512(); + address generate_pshuffle_byte_flip_mask_sha512(address& entry_ymm_lo); address generate_upper_word_mask(); address generate_shuffle_byte_flip_mask(); - address generate_pshuffle_byte_flip_mask(); + address generate_pshuffle_byte_flip_mask(address& entry_00ba, address& entry_dc0); // AES intrinsic stubs @@ -650,8 +650,33 @@ class StubGenerator: public StubCodeGenerator { void generate_compiler_stubs(); void generate_final_stubs(); +#if INCLUDE_CDS + static void init_AOTAddressTable_adler(GrowableArray
& external_addresses); + static void init_AOTAddressTable_aes(GrowableArray
& external_addresses); + static void init_AOTAddressTable_cbrt(GrowableArray
& external_addresses); + static void init_AOTAddressTable_chacha(GrowableArray
& external_addresses); + static void init_AOTAddressTable_constants(GrowableArray
& external_addresses); + static void init_AOTAddressTable_dilithium(GrowableArray
& external_addresses); + static void init_AOTAddressTable_exp(GrowableArray
& external_addresses); + static void init_AOTAddressTable_fmod(GrowableArray
& external_addresses); + static void init_AOTAddressTable_ghash(GrowableArray
& external_addresses); + static void init_AOTAddressTable_kyber(GrowableArray
& external_addresses); + static void init_AOTAddressTable_log(GrowableArray
& external_addresses); + static void init_AOTAddressTable_poly1305(GrowableArray
& external_addresses); + static void init_AOTAddressTable_poly_mont(GrowableArray
& external_addresses); + static void init_AOTAddressTable_pow(GrowableArray
& external_addresses); + static void init_AOTAddressTable_sha3(GrowableArray
& external_addresses); + static void init_AOTAddressTable_sin(GrowableArray
& external_addresses); + static void init_AOTAddressTable_sinh(GrowableArray
& external_addresses); + static void init_AOTAddressTable_tan(GrowableArray
& external_addresses); + static void init_AOTAddressTable_tanh(GrowableArray
& external_addresses); +#endif // INCLUDE_CDS + public: - StubGenerator(CodeBuffer* code, BlobId blob_id); + StubGenerator(CodeBuffer* code, BlobId blob_id, AOTStubData* stub_data); +#if INCLUDE_CDS + static void init_AOTAddressTable(GrowableArray
& external_addresses); +#endif // INCLUDE_CDS }; #endif // CPU_X86_STUBGENERATOR_X86_64_HPP diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_adler.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_adler.cpp index 1d3e7afde1d..a9424978e0e 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_adler.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_adler.cpp @@ -67,8 +67,14 @@ address StubGenerator::generate_updateBytesAdler32() { __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_updateBytesAdler32_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); // Choose an appropriate LIMIT for inner loop based on the granularity // of intermediate results. For int, LIMIT of 5552 will ensure intermediate @@ -334,7 +340,19 @@ address StubGenerator::generate_updateBytesAdler32() { __ leave(); __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); return start; } #undef __ + +#if INCLUDE_CDS +void StubGenerator::init_AOTAddressTable_adler(GrowableArray
& external_addresses) { +#define ADD(addr) external_addresses.append((address)(addr)) + ADD(ADLER32_ASCALE_TABLE); + ADD(ADLER32_SHUF0_TABLE); + ADD(ADLER32_SHUF1_TABLE); +#undef ADD +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_aes.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_aes.cpp index 1fa80c9d967..b95aa5f8818 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_aes.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_aes.cpp @@ -220,7 +220,7 @@ void StubGenerator::generate_aes_stubs() { StubRoutines::_cipherBlockChaining_decryptAESCrypt = generate_cipherBlockChaining_decryptAESCrypt_Parallel(); StubRoutines::_electronicCodeBook_encryptAESCrypt = generate_electronicCodeBook_encryptAESCrypt_Parallel(); StubRoutines::_electronicCodeBook_decryptAESCrypt = generate_electronicCodeBook_decryptAESCrypt_Parallel(); - if (VM_Version::supports_avx2()) { + if (VM_Version::supports_avx2() && VM_Version::supports_clmul()) { StubRoutines::_galoisCounterMode_AESCrypt = generate_avx2_galoisCounterMode_AESCrypt(); } } @@ -250,10 +250,16 @@ void StubGenerator::generate_aes_stubs() { // Output: // rax - number of processed bytes address StubGenerator::generate_galoisCounterMode_AESCrypt() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_galoisCounterMode_AESCrypt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); const Register in = c_rarg0; const Register len = c_rarg1; @@ -319,6 +325,9 @@ address StubGenerator::generate_galoisCounterMode_AESCrypt() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -337,10 +346,16 @@ address StubGenerator::generate_galoisCounterMode_AESCrypt() { // Output: // rax - number of processed bytes address StubGenerator::generate_avx2_galoisCounterMode_AESCrypt() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_galoisCounterMode_AESCrypt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); const Register in = c_rarg0; const Register len = c_rarg1; @@ -404,15 +419,24 @@ address StubGenerator::generate_avx2_galoisCounterMode_AESCrypt() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } // Vector AES Counter implementation address StubGenerator::generate_counterMode_VectorAESCrypt() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_counterMode_AESCrypt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); const Register from = c_rarg0; // source array address const Register to = c_rarg1; // destination array address @@ -471,6 +495,9 @@ address StubGenerator::generate_counterMode_VectorAESCrypt() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -498,10 +525,16 @@ address StubGenerator::generate_counterMode_VectorAESCrypt() { // address StubGenerator::generate_counterMode_AESCrypt_Parallel() { assert(UseAES, "need AES instructions and misaligned SSE support"); - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_counterMode_AESCrypt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); const Register from = c_rarg0; // source array address const Register to = c_rarg1; // destination array address @@ -781,15 +814,24 @@ address StubGenerator::generate_counterMode_AESCrypt_Parallel() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_cipherBlockChaining_decryptVectorAESCrypt() { assert(VM_Version::supports_avx512_vaes(), "need AES instructions and misaligned SSE support"); - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_cipherBlockChaining_decryptAESCrypt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); const Register from = c_rarg0; // source array address const Register to = c_rarg1; // destination array address @@ -1057,6 +1099,9 @@ address StubGenerator::generate_cipherBlockChaining_decryptVectorAESCrypt() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -1069,11 +1114,17 @@ address StubGenerator::generate_cipherBlockChaining_decryptVectorAESCrypt() { // address StubGenerator::generate_aescrypt_encryptBlock() { assert(UseAES, "need AES instructions and misaligned SSE support"); - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_aescrypt_encryptBlock_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); Label L_doLast; - address start = __ pc(); + start = __ pc(); const Register from = c_rarg0; // source array address const Register to = c_rarg1; // destination array address @@ -1152,6 +1203,9 @@ address StubGenerator::generate_aescrypt_encryptBlock() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -1164,11 +1218,17 @@ address StubGenerator::generate_aescrypt_encryptBlock() { // address StubGenerator::generate_aescrypt_decryptBlock() { assert(UseAES, "need AES instructions and misaligned SSE support"); - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_aescrypt_decryptBlock_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); Label L_doLast; - address start = __ pc(); + start = __ pc(); const Register from = c_rarg0; // source array address const Register to = c_rarg1; // destination array address @@ -1248,6 +1308,9 @@ address StubGenerator::generate_aescrypt_decryptBlock() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -1266,10 +1329,16 @@ address StubGenerator::generate_aescrypt_decryptBlock() { // address StubGenerator::generate_cipherBlockChaining_encryptAESCrypt() { assert(UseAES, "need AES instructions and misaligned SSE support"); - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_cipherBlockChaining_encryptAESCrypt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label L_exit, L_key_192_256, L_key_256, L_loopTop_128, L_loopTop_192, L_loopTop_256; const Register from = c_rarg0; // source array address @@ -1398,6 +1467,9 @@ address StubGenerator::generate_cipherBlockChaining_encryptAESCrypt() { __ jcc(Assembler::notEqual, L_loopTop_256); __ jmp(L_exit); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -1422,11 +1494,15 @@ address StubGenerator::generate_cipherBlockChaining_encryptAESCrypt() { // address StubGenerator::generate_electronicCodeBook_AESCrypt_Parallel(bool is_encrypt) { assert(UseAES, "need AES instructions and misaligned SSE support"); - __ align(CodeEntryAlignment); StubId stub_id = is_encrypt ? StubId::stubgen_electronicCodeBook_encryptAESCrypt_id : StubId::stubgen_electronicCodeBook_decryptAESCrypt_id; + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); const Register from = c_rarg0; // source array address const Register to = c_rarg1; // destination array address @@ -1581,6 +1657,9 @@ __ opc(xmm_result0, reg); __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; #undef DoFour @@ -1612,10 +1691,16 @@ address StubGenerator::generate_electronicCodeBook_decryptAESCrypt_Parallel() { // address StubGenerator::generate_cipherBlockChaining_decryptAESCrypt_Parallel() { assert(UseAES, "need AES instructions and misaligned SSE support"); - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_cipherBlockChaining_decryptAESCrypt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); const Register from = c_rarg0; // source array address const Register to = c_rarg1; // destination array address @@ -1851,14 +1936,23 @@ __ opc(xmm_result3, src_reg); __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_electronicCodeBook_encryptAESCrypt() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_electronicCodeBook_encryptAESCrypt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); const Register from = c_rarg0; // source array address const Register to = c_rarg1; // destination array address @@ -1872,14 +1966,23 @@ address StubGenerator::generate_electronicCodeBook_encryptAESCrypt() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_electronicCodeBook_decryptAESCrypt() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_electronicCodeBook_decryptAESCrypt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); const Register from = c_rarg0; // source array address const Register to = c_rarg1; // destination array address @@ -1893,6 +1996,9 @@ address StubGenerator::generate_electronicCodeBook_decryptAESCrypt() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -4292,3 +4398,27 @@ void StubGenerator::aesgcm_avx2(Register in, Register len, Register ct, Register } #undef __ + +#if INCLUDE_CDS +void StubGenerator::init_AOTAddressTable_aes(GrowableArray
& external_addresses) { +#define ADD(addr) external_addresses.append((address)(addr)) + ADD(key_shuffle_mask_addr()); + ADD(counter_shuffle_mask_addr()); + ADD(counter_mask_linc0_addr()); + ADD(counter_mask_linc1_addr()); + ADD(counter_mask_linc1f_addr()); + ADD(counter_mask_linc2_addr()); + ADD(counter_mask_linc2f_addr()); + ADD(counter_mask_linc4_addr()); + ADD(counter_mask_linc8_addr()); + ADD(counter_mask_linc16_addr()); + ADD(counter_mask_linc32_addr()); + ADD(counter_mask_ones_addr()); + ADD(ghash_polynomial_reduction_addr()); + ADD(ghash_polynomial_two_one_addr()); + ADD(counter_mask_addbe_4444_addr()); + ADD(counter_mask_addbe_1234_addr()); + ADD(counter_mask_add_1234_addr()); +#undef ADD +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_arraycopy.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_arraycopy.cpp index 01e004b7b43..5530e5325de 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_arraycopy.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_arraycopy.cpp @@ -570,10 +570,45 @@ address StubGenerator::generate_disjoint_copy_avx3_masked(StubId stub_id, addres default: ShouldNotReachHere(); } + GrowableArray
entries; + GrowableArray
extras; + bool add_handlers = !is_oop && !aligned; + bool add_relocs = UseZGC && is_oop; + bool add_extras = add_handlers || add_relocs; + // The stub employs one unsafe handler region by default but has two + // when MaxVectorSize == 64 So we may expect 0, 3 or 6 extras. + int handlers_count = (MaxVectorSize == 64 ? 2 : 1); + int expected_entry_count = (entry != nullptr ? 2 : 1); + int expected_extra_count = (add_handlers ? handlers_count : 0) * UnsafeMemoryAccess::COLUMN_COUNT; // 0/1/2 x UMAM {start,end,handler} + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == expected_entry_count, "sanity check"); + GrowableArray
* entries_ptr = (entry_count == 1 ? nullptr : &entries); + GrowableArray
* extras_ptr = (add_extras ? &extras : nullptr); + address start = load_archive_data(stub_id, entries_ptr, extras_ptr); + if (start != nullptr) { + assert(entries.length() == expected_entry_count - 1, + "unexpected entry count %d", entries.length()); + assert(!add_handlers || extras.length() == expected_extra_count, + "unexpected handler addresses count %d", extras.length()); + if (entry != nullptr) { + *entry = entries.at(0); + } + if (add_handlers) { + // restore 1/2 x UMAM {start,end,handler} addresses from extras + register_unsafe_access_handlers(extras, 0, handlers_count); + } +#if INCLUDE_ZGC + // register addresses at which ZGC does colour patching + if (add_relocs) { + register_reloc_addresses(extras, 0, extras.length()); + } +#endif // INCLUDE_ZGC + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); bool use64byteVector = (MaxVectorSize > 32) && (CopyAVX3Threshold == 0); const int large_threshold = 2621440; // 2.5 MB @@ -595,6 +630,7 @@ address StubGenerator::generate_disjoint_copy_avx3_masked(StubId stub_id, addres if (entry != nullptr) { *entry = __ pc(); + entries.append(*entry); // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) BLOCK_COMMENT("Entry:"); } @@ -620,7 +656,7 @@ address StubGenerator::generate_disjoint_copy_avx3_masked(StubId stub_id, addres int threshold[] = { 4096, 2048, 1024, 512}; // UnsafeMemoryAccess page error: continue after unsafe access - UnsafeMemoryAccessMark umam(this, !is_oop && !aligned, true); + UnsafeMemoryAccessMark umam(this, add_handlers, true); // 'from', 'to' and 'count' are now valid // temp1 holds remaining count and temp4 holds running count used to compute @@ -789,10 +825,28 @@ address StubGenerator::generate_disjoint_copy_avx3_masked(StubId stub_id, addres if (MaxVectorSize == 64) { __ BIND(L_copy_large); - UnsafeMemoryAccessMark umam(this, !is_oop && !aligned, false, ucme_exit_pc); + UnsafeMemoryAccessMark umam(this, add_handlers, false, ucme_exit_pc); arraycopy_avx3_large(to, from, temp1, temp2, temp3, temp4, count, xmm1, xmm2, xmm3, xmm4, shift); __ jmp(L_finish); } + // retrieve the registered handler addresses + address end = __ pc(); + if (add_handlers) { + retrieve_unsafe_access_handlers(start, end, extras); + } + assert(extras.length() == expected_extra_count, + "unexpected handler addresses count %d", extras.length()); +#if INCLUDE_ZGC + // retrieve addresses at which ZGC does colour patching + if (add_relocs) { + retrieve_reloc_addresses(start, end, extras); + } +#endif // INCLUDE_ZGC + + // record the stub entry and end plus the no_push entry and any + // extra handler addresses + store_archive_data(stub_id, start, end, entries_ptr, extras_ptr); + return start; } @@ -907,10 +961,41 @@ address StubGenerator::generate_conjoint_copy_avx3_masked(StubId stub_id, addres default: ShouldNotReachHere(); } - + GrowableArray
entries; + GrowableArray
extras; + bool add_handlers = !is_oop && !aligned; + bool add_relocs = UseZGC && is_oop; + bool add_extras = add_handlers || add_relocs; + int expected_entry_count = (entry != nullptr ? 2 : 1); + int expected_handler_count = (add_handlers ? 1 : 0) * UnsafeMemoryAccess::COLUMN_COUNT; // 0/1 x UMAM {start,end,handler} + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == expected_entry_count, "sanity check"); + GrowableArray
* entries_ptr = (entry_count == 1 ? nullptr : &entries); + GrowableArray
* extras_ptr = (add_extras ? &extras : nullptr); + address start = load_archive_data(stub_id, entries_ptr, extras_ptr); + if (start != nullptr) { + assert(entries.length() == expected_entry_count - 1, + "unexpected entry count %d", entries.length()); + assert(!add_handlers || extras.length() == expected_handler_count, + "unexpected handler addresses count %d", extras.length()); + if (entry != nullptr) { + *entry = entries.at(0); + } + if (add_handlers) { + // restore 1 x UMAM {start,end,handler} addresses from extras + register_unsafe_access_handlers(extras, 0, 1); + } +#if INCLUDE_ZGC + if (add_relocs) { + // register addresses at which ZGC does colour patching + register_reloc_addresses(extras, 0, extras.length()); + } +#endif // INCLUDE_ZGC + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); bool use64byteVector = (MaxVectorSize > 32) && (CopyAVX3Threshold == 0); @@ -931,6 +1016,7 @@ address StubGenerator::generate_conjoint_copy_avx3_masked(StubId stub_id, addres if (entry != nullptr) { *entry = __ pc(); + entries.append(*entry); // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) BLOCK_COMMENT("Entry:"); } @@ -957,7 +1043,7 @@ address StubGenerator::generate_conjoint_copy_avx3_masked(StubId stub_id, addres int threshold[] = { 4096, 2048, 1024, 512}; // UnsafeMemoryAccess page error: continue after unsafe access - UnsafeMemoryAccessMark umam(this, !is_oop && !aligned, true); + UnsafeMemoryAccessMark umam(this, add_handlers, true); // 'from', 'to' and 'count' are now valid // temp1 holds remaining count. @@ -1071,6 +1157,23 @@ address StubGenerator::generate_conjoint_copy_avx3_masked(StubId stub_id, addres __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // retrieve the registered handler addresses + address end = __ pc(); + if (add_handlers) { + retrieve_unsafe_access_handlers(start, end, extras); + } + assert(extras.length() == expected_handler_count, + "unexpected handler addresses count %d", extras.length()); +#if INCLUDE_ZGC + // retrieve addresses at which ZGC does colour patching + if (add_relocs) { + retrieve_reloc_addresses(start, end, extras); + } +#endif // INCLUDE_ZGC + // record the stub entry and end plus the no_push entry and any + // extra handler addresses + store_archive_data(stub_id, start, end, entries_ptr, extras_ptr); + return start; } @@ -1385,9 +1488,29 @@ address StubGenerator::generate_disjoint_byte_copy(address* entry) { return generate_disjoint_copy_avx3_masked(stub_id, entry); } #endif + GrowableArray
entries; + GrowableArray
extras; + int expected_entry_count = (entry != nullptr ? 2 : 1); + int expected_handler_count = (2 * UnsafeMemoryAccess::COLUMN_COUNT); // 2 x UMAM {start,end,handler} + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == expected_entry_count, "sanity check"); + GrowableArray
* entries_ptr = (entry_count == 1 ? nullptr : &entries); + address start = load_archive_data(stub_id, entries_ptr, &extras); + if (start != nullptr) { + assert(entries.length() == expected_entry_count - 1, + "unexpected entry count %d", entries.length()); + assert(extras.length() == expected_handler_count, + "unexpected handler addresses count %d", extras.length()); + if (entry != nullptr) { + *entry = entries.at(0); + } + // restore 2 UMAM {start,end,handler} addresses from extras + register_unsafe_access_handlers(extras, 0, 2); + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); DecoratorSet decorators = IN_HEAP | IS_ARRAY | ARRAYCOPY_DISJOINT; Label L_copy_bytes, L_copy_8_bytes, L_copy_4_bytes, L_copy_2_bytes; @@ -1407,6 +1530,7 @@ address StubGenerator::generate_disjoint_byte_copy(address* entry) { if (entry != nullptr) { *entry = __ pc(); + entries.append(*entry); // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) BLOCK_COMMENT("Entry:"); } @@ -1476,6 +1600,17 @@ __ BIND(L_exit); copy_bytes_forward(end_from, end_to, qword_count, rax, r10, L_copy_bytes, L_copy_8_bytes, decorators, T_BYTE); __ jmp(L_copy_4_bytes); } + + // retrieve the registered handler addresses + address end = __ pc(); + retrieve_unsafe_access_handlers(start, end, extras); + assert(extras.length() == expected_handler_count, + "unexpected handler addresses count %d", extras.length()); + + // record the stub entry and end plus the no_push entry and any + // extra handler addresses + store_archive_data(stub_id, start, end, entries_ptr, &extras); + return start; } @@ -1503,9 +1638,29 @@ address StubGenerator::generate_conjoint_byte_copy(address nooverlap_target, add return generate_conjoint_copy_avx3_masked(stub_id, entry, nooverlap_target); } #endif + GrowableArray
entries; + GrowableArray
extras; + int expected_entry_count = (entry != nullptr ? 2 : 1); + int expected_handler_count = (2 * UnsafeMemoryAccess::COLUMN_COUNT); // 2 x UMAM {start,end,handler} + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == expected_entry_count, "sanity check"); + GrowableArray
* entries_ptr = (entry_count == 1 ? nullptr : &entries); + address start = load_archive_data(stub_id, entries_ptr, &extras); + if (start != nullptr) { + assert(entries.length() == expected_entry_count - 1, + "unexpected entry count %d", entries.length()); + assert(extras.length() == expected_handler_count, + "unexpected handler addresses count %d", extras.length()); + if (entry != nullptr) { + *entry = entries.at(0); + } + // restore 2 UMAM {start,end,handler} addresses from extras + register_unsafe_access_handlers(extras, 0, 2); + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); DecoratorSet decorators = IN_HEAP | IS_ARRAY; Label L_copy_bytes, L_copy_8_bytes, L_copy_4_bytes, L_copy_2_bytes; @@ -1520,6 +1675,7 @@ address StubGenerator::generate_conjoint_byte_copy(address nooverlap_target, add if (entry != nullptr) { *entry = __ pc(); + entries.append(*entry); // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) BLOCK_COMMENT("Entry:"); } @@ -1586,6 +1742,16 @@ address StubGenerator::generate_conjoint_byte_copy(address nooverlap_target, add __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // retrieve the registered handler addresses + address end = __ pc(); + retrieve_unsafe_access_handlers(start, end, extras); + assert(extras.length() == expected_handler_count, + "unexpected handler addresses count %d", extras.length()); + + // record the stub entry and end plus the no_push entry and any + // extra handler addresses + store_archive_data(stub_id, start, end, entries_ptr, &extras); + return start; } @@ -1616,10 +1782,29 @@ address StubGenerator::generate_disjoint_short_copy(address *entry) { return generate_disjoint_copy_avx3_masked(stub_id, entry); } #endif - + GrowableArray
entries; + GrowableArray
extras; + int expected_entry_count = (entry != nullptr ? 2 : 1); + int expected_handler_count = (2 * UnsafeMemoryAccess::COLUMN_COUNT); // 2 x UMAM {start,end,handler} + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == expected_entry_count, "sanity check"); + GrowableArray
* entries_ptr = (entry_count == 1 ? nullptr : &entries); + address start = load_archive_data(stub_id, entries_ptr, &extras); + if (start != nullptr) { + assert(entries.length() == expected_entry_count - 1, + "unexpected entry count %d", entries.length()); + assert(extras.length() == expected_handler_count, + "unexpected handler addresses count %d", extras.length()); + if (entry != nullptr) { + *entry = entries.at(0); + } + // restore 2 UMAM {start,end,handler} addresses from extras + register_unsafe_access_handlers(extras, 0, 2); + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); DecoratorSet decorators = IN_HEAP | IS_ARRAY | ARRAYCOPY_DISJOINT; Label L_copy_bytes, L_copy_8_bytes, L_copy_4_bytes,L_copy_2_bytes,L_exit; @@ -1638,6 +1823,7 @@ address StubGenerator::generate_disjoint_short_copy(address *entry) { if (entry != nullptr) { *entry = __ pc(); + entries.append(*entry); // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) BLOCK_COMMENT("Entry:"); } @@ -1701,6 +1887,16 @@ __ BIND(L_exit); __ jmp(L_copy_4_bytes); } + // retrieve the registered handler addresses + address end = __ pc(); + retrieve_unsafe_access_handlers(start, end, extras); + assert(extras.length() == expected_handler_count, + "unexpected handler addresses count %d", extras.length()); + + // record the stub entry and end plus the no_push entry and any + // extra handler addresses + store_archive_data(stub_id, start, end, entries_ptr, &extras); + return start; } @@ -1708,7 +1904,6 @@ __ BIND(L_exit); address StubGenerator::generate_fill(StubId stub_id) { BasicType t; bool aligned; - switch (stub_id) { case StubId::stubgen_jbyte_fill_id: t = T_BYTE; @@ -1737,10 +1932,27 @@ address StubGenerator::generate_fill(StubId stub_id) { default: ShouldNotReachHere(); } + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + GrowableArray
extras; + bool add_handlers = ((t == T_BYTE) && !aligned); + int handlers_count = (add_handlers ? 1 : 0); + int expected_extras_count = (handlers_count * UnsafeMemoryAccess::COLUMN_COUNT); // 0/1 x UMAM {start,end,handler} + GrowableArray
* extras_ptr = (add_handlers ? &extras : nullptr); + address start = load_archive_data(stub_id, nullptr, extras_ptr); + if (start != nullptr) { + assert(extras.length() == expected_extras_count, + "unexpected handler addresses count %d", extras.length()); + if (add_handlers) { + // restore 1 x UMAM {start,end,handler} addresses from extras + register_unsafe_access_handlers(extras, 0, 1); + } + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); BLOCK_COMMENT("Entry:"); @@ -1753,7 +1965,7 @@ address StubGenerator::generate_fill(StubId stub_id) { { // Add set memory mark to protect against unsafe accesses faulting - UnsafeMemoryAccessMark umam(this, ((t == T_BYTE) && !aligned), true); + UnsafeMemoryAccessMark umam(this, add_handlers, true); __ generate_fill(t, aligned, to, value, r11, rax, xmm0); } @@ -1761,6 +1973,15 @@ address StubGenerator::generate_fill(StubId stub_id) { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + address end = __ pc(); + if (add_handlers) { + retrieve_unsafe_access_handlers(start, end, extras); + } + assert(extras.length() == expected_extras_count, + "unexpected handler addresses count %d", extras.length()); + // record the stub entry and end + store_archive_data(stub_id, start, end, nullptr, extras_ptr); + return start; } @@ -1788,10 +2009,29 @@ address StubGenerator::generate_conjoint_short_copy(address nooverlap_target, ad return generate_conjoint_copy_avx3_masked(stub_id, entry, nooverlap_target); } #endif - + GrowableArray
entries; + GrowableArray
extras; + int expected_entry_count = (entry != nullptr ? 2 : 1); + int expected_handler_count = (2 * UnsafeMemoryAccess::COLUMN_COUNT); // 2 x UMAM {start,end,handler} + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == expected_entry_count, "sanity check"); + GrowableArray
* entries_ptr = (entry_count == 1 ? nullptr : &entries); + address start = load_archive_data(stub_id, entries_ptr, &extras); + if (start != nullptr) { + assert(entries.length() == expected_entry_count - 1, + "unexpected entry count %d", entries.length()); + assert(extras.length() == expected_handler_count, + "unexpected handler addresses count %d", extras.length()); + if (entry != nullptr) { + *entry = entries.at(0); + } + // restore 2 UMAM {start,end,handler} addresses from extras + register_unsafe_access_handlers(extras, 0, 2); + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); DecoratorSet decorators = IN_HEAP | IS_ARRAY; Label L_copy_bytes, L_copy_8_bytes, L_copy_4_bytes; @@ -1806,6 +2046,7 @@ address StubGenerator::generate_conjoint_short_copy(address nooverlap_target, ad if (entry != nullptr) { *entry = __ pc(); + entries.append(*entry); // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) BLOCK_COMMENT("Entry:"); } @@ -1864,6 +2105,16 @@ address StubGenerator::generate_conjoint_short_copy(address nooverlap_target, ad __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // retrieve the registered handler addresses + address end = __ pc(); + retrieve_unsafe_access_handlers(start, end, extras); + assert(extras.length() == expected_handler_count, + "unexpected handler addresses count %d", extras.length()); + + // record the stub entry and end plus the no_push entry and any + // extra handler addresses + store_archive_data(stub_id, start, end, entries_ptr, &extras); + return start; } @@ -1916,10 +2167,42 @@ address StubGenerator::generate_disjoint_int_oop_copy(StubId stub_id, address* e return generate_disjoint_copy_avx3_masked(stub_id, entry); } #endif + GrowableArray
entries; + GrowableArray
extras; + bool add_handlers = !is_oop && !aligned; + bool add_relocs = UseZGC && is_oop; + bool add_extras = add_handlers || add_relocs; + int expected_entry_count = (entry != nullptr ? 2 : 1); + int expected_handler_count = (add_handlers ? 2 : 0) * UnsafeMemoryAccess::COLUMN_COUNT; // 0/2 x UMAM {start,end,handler} + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == expected_entry_count, "sanity check"); + GrowableArray
* entries_ptr = (entry_count == 1 ? nullptr : &entries); + GrowableArray
* extras_ptr = (add_extras ? &extras : nullptr); + address start = load_archive_data(stub_id, entries_ptr, extras_ptr); + if (start != nullptr) { + assert(entries.length() == expected_entry_count - 1, + "unexpected entry count %d", entries.length()); + assert(!add_handlers || extras.length() == expected_handler_count, + "unexpected handler addresses count %d", extras.length()); + if (entry != nullptr) { + *entry = entries.at(0); + } + if (add_handlers) { + // restore 2 UMAM {start,end,handler} addresses from extras + register_unsafe_access_handlers(extras, 0, 2); + } +#if INCLUDE_ZGC + // register addresses at which ZGC does colour patching + if (add_relocs) { + register_reloc_addresses(extras, 0, extras.length()); + } +#endif // INCLUDE_ZGC + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label L_copy_bytes, L_copy_8_bytes, L_copy_4_bytes, L_exit; const Register from = rdi; // source array address @@ -1937,6 +2220,7 @@ address StubGenerator::generate_disjoint_int_oop_copy(StubId stub_id, address* e if (entry != nullptr) { *entry = __ pc(); + entries.append(*entry); // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) BLOCK_COMMENT("Entry:"); } @@ -1957,7 +2241,7 @@ address StubGenerator::generate_disjoint_int_oop_copy(StubId stub_id, address* e { // UnsafeMemoryAccess page error: continue after unsafe access - UnsafeMemoryAccessMark umam(this, !is_oop && !aligned, true); + UnsafeMemoryAccessMark umam(this, add_handlers, true); // 'from', 'to' and 'count' are now valid __ movptr(dword_count, count); __ shrptr(count, 1); // count => qword_count @@ -1969,20 +2253,20 @@ address StubGenerator::generate_disjoint_int_oop_copy(StubId stub_id, address* e __ jmp(L_copy_bytes); // Copy trailing qwords - __ BIND(L_copy_8_bytes); + __ BIND(L_copy_8_bytes); __ movq(rax, Address(end_from, qword_count, Address::times_8, 8)); __ movq(Address(end_to, qword_count, Address::times_8, 8), rax); __ increment(qword_count); __ jcc(Assembler::notZero, L_copy_8_bytes); // Check for and copy trailing dword - __ BIND(L_copy_4_bytes); + __ BIND(L_copy_4_bytes); __ testl(dword_count, 1); // Only byte test since the value is 0 or 1 __ jccb(Assembler::zero, L_exit); __ movl(rax, Address(end_from, 8)); __ movl(Address(end_to, 8), rax); } -__ BIND(L_exit); + __ BIND(L_exit); address ucme_exit_pc = __ pc(); bs->arraycopy_epilogue(_masm, decorators, type, from, to, dword_count); restore_arg_regs_using_thread(); @@ -1993,12 +2277,30 @@ __ BIND(L_exit); __ ret(0); { - UnsafeMemoryAccessMark umam(this, !is_oop && !aligned, false, ucme_exit_pc); + UnsafeMemoryAccessMark umam(this, add_handlers, false, ucme_exit_pc); // Copy in multi-bytes chunks copy_bytes_forward(end_from, end_to, qword_count, rax, r10, L_copy_bytes, L_copy_8_bytes, decorators, is_oop ? T_OBJECT : T_INT); __ jmp(L_copy_4_bytes); } + // retrieve the registered handler addresses + address end = __ pc(); + if (add_handlers) { + retrieve_unsafe_access_handlers(start, end, extras); + } + assert(extras.length() == expected_handler_count, + "unexpected handler addresses count %d", extras.length()); +#if INCLUDE_ZGC + // retrieve addresses at which ZGC does colour patching + if (add_relocs) { + retrieve_reloc_addresses(start, end, extras); + } +#endif // INCLUDE_ZGC + + // record the stub entry and end plus the no_push entry and any + // extra handler addresses + store_archive_data(stub_id, start, end, entries_ptr, extras_ptr); + return start; } @@ -2047,10 +2349,42 @@ address StubGenerator::generate_conjoint_int_oop_copy(StubId stub_id, address no return generate_conjoint_copy_avx3_masked(stub_id, entry, nooverlap_target); } #endif + bool add_handlers = !is_oop && !aligned; + bool add_relocs = UseZGC && is_oop; + bool add_extras = add_handlers || add_relocs; + GrowableArray
entries; + GrowableArray
extras; + int expected_entry_count = (entry != nullptr ? 2 : 1); + int expected_handler_count = (add_handlers ? 2 : 0) * UnsafeMemoryAccess::COLUMN_COUNT; // 0/2 x UMAM {start,end,handler} + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == expected_entry_count, "sanity check"); + GrowableArray
* entries_ptr = (entry_count == 1 ? nullptr : &entries); + GrowableArray
* extras_ptr = (add_extras ? &extras : nullptr); + address start = load_archive_data(stub_id, entries_ptr, extras_ptr); + if (start != nullptr) { + assert(entries.length() == expected_entry_count - 1, + "unexpected entry count %d", entries.length()); + assert(!add_handlers || extras.length() == expected_handler_count, + "unexpected handler addresses count %d", extras.length()); + if (entry != nullptr) { + *entry = entries.at(0); + } + if (add_handlers) { + // restore 2 UMAM {start,end,handler} addresses from extras + register_unsafe_access_handlers(extras, 0, 2); + } +#if INCLUDE_ZGC + // register addresses at which ZGC does colour patching + if (add_relocs) { + register_reloc_addresses(extras, 6, extras.length()); + } +#endif // INCLUDE_ZGC + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label L_copy_bytes, L_copy_8_bytes, L_exit; const Register from = rdi; // source array address @@ -2064,7 +2398,8 @@ address StubGenerator::generate_conjoint_int_oop_copy(StubId stub_id, address no if (entry != nullptr) { *entry = __ pc(); - // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) + entries.append(*entry); + // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) BLOCK_COMMENT("Entry:"); } @@ -2087,7 +2422,7 @@ address StubGenerator::generate_conjoint_int_oop_copy(StubId stub_id, address no assert_clean_int(count, rax); // Make sure 'count' is clean int. { // UnsafeMemoryAccess page error: continue after unsafe access - UnsafeMemoryAccessMark umam(this, !is_oop && !aligned, true); + UnsafeMemoryAccessMark umam(this, add_handlers, true); // 'from', 'to' and 'count' are now valid __ movptr(dword_count, count); __ shrptr(count, 1); // count => qword_count @@ -2102,7 +2437,7 @@ address StubGenerator::generate_conjoint_int_oop_copy(StubId stub_id, address no __ jmp(L_copy_bytes); // Copy trailing qwords - __ BIND(L_copy_8_bytes); + __ BIND(L_copy_8_bytes); __ movq(rax, Address(from, qword_count, Address::times_8, -8)); __ movq(Address(to, qword_count, Address::times_8, -8), rax); __ decrement(qword_count); @@ -2120,12 +2455,12 @@ address StubGenerator::generate_conjoint_int_oop_copy(StubId stub_id, address no { // UnsafeMemoryAccess page error: continue after unsafe access - UnsafeMemoryAccessMark umam(this, !is_oop && !aligned, true); + UnsafeMemoryAccessMark umam(this, add_handlers, true); // Copy in multi-bytes chunks copy_bytes_backward(from, to, qword_count, rax, r10, L_copy_bytes, L_copy_8_bytes, decorators, is_oop ? T_OBJECT : T_INT); } -__ BIND(L_exit); + __ BIND(L_exit); bs->arraycopy_epilogue(_masm, decorators, type, from, to, dword_count); restore_arg_regs_using_thread(); INC_COUNTER_NP(SharedRuntime::_jint_array_copy_ctr, rscratch1); // Update counter after rscratch1 is free @@ -2134,6 +2469,23 @@ __ BIND(L_exit); __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // retrieve the registered handler addresses + address end = __ pc(); + if (add_handlers) { + retrieve_unsafe_access_handlers(start, end, extras); + } + assert(extras.length() == expected_handler_count, + "unexpected handler addresses count %d", extras.length()); +#if INCLUDE_ZGC + // retrieve addresses at which ZGC does colour patching + if (add_relocs) { + retrieve_reloc_addresses(start, end, extras); + } +#endif // INCLUDE_ZGC + // record the stub entry and end plus the no_push entry and any + // extra handler addresses + store_archive_data(stub_id, start, end, entries_ptr, extras_ptr); + return start; } @@ -2180,10 +2532,42 @@ address StubGenerator::generate_disjoint_long_oop_copy(StubId stub_id, address * return generate_disjoint_copy_avx3_masked(stub_id, entry); } #endif + bool add_handlers = !is_oop && !aligned; + bool add_relocs = UseZGC && is_oop; + bool add_extras = add_handlers || add_relocs; + GrowableArray
entries; + GrowableArray
extras; + int expected_entry_count = (entry != nullptr ? 2 : 1); + int expected_handler_count = (add_handlers ? 2 : 0) * UnsafeMemoryAccess::COLUMN_COUNT; // 0/2 x UMAM {start,end,handler} + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == expected_entry_count, "sanity check"); + GrowableArray
* entries_ptr = (entry_count == 1 ? nullptr : &entries); + GrowableArray
* extras_ptr = (add_extras ? &extras : nullptr); + address start = load_archive_data(stub_id, entries_ptr, extras_ptr); + if (start != nullptr) { + assert(entries.length() == expected_entry_count - 1, + "unexpected entry count %d", entries.length()); + assert(!add_handlers || extras.length() == expected_handler_count, + "unexpected handler addresses count %d", extras.length()); + if (entry != nullptr) { + *entry = entries.at(0); + } + if (add_handlers) { + // restore 2 UMAM {start,end,handler} addresses from extras + register_unsafe_access_handlers(extras, 0, 2); + } +#if INCLUDE_ZGC + // register addresses at which ZGC does colour patching + if (add_relocs) { + register_reloc_addresses(extras, 0, extras.length()); + } +#endif // INCLUDE_ZGC + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label L_copy_bytes, L_copy_8_bytes, L_exit; const Register from = rdi; // source array address @@ -2201,6 +2585,7 @@ address StubGenerator::generate_disjoint_long_oop_copy(StubId stub_id, address * if (entry != nullptr) { *entry = __ pc(); + entries.append(*entry); // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) BLOCK_COMMENT("Entry:"); } @@ -2221,7 +2606,7 @@ address StubGenerator::generate_disjoint_long_oop_copy(StubId stub_id, address * bs->arraycopy_prologue(_masm, decorators, type, from, to, qword_count); { // UnsafeMemoryAccess page error: continue after unsafe access - UnsafeMemoryAccessMark umam(this, !is_oop && !aligned, true); + UnsafeMemoryAccessMark umam(this, add_handlers, true); // Copy from low to high addresses. Use 'to' as scratch. __ lea(end_from, Address(from, qword_count, Address::times_8, -8)); @@ -2253,7 +2638,7 @@ address StubGenerator::generate_disjoint_long_oop_copy(StubId stub_id, address * { // UnsafeMemoryAccess page error: continue after unsafe access - UnsafeMemoryAccessMark umam(this, !is_oop && !aligned, true); + UnsafeMemoryAccessMark umam(this, add_handlers, true); // Copy in multi-bytes chunks copy_bytes_forward(end_from, end_to, qword_count, rax, r10, L_copy_bytes, L_copy_8_bytes, decorators, is_oop ? T_OBJECT : T_LONG); } @@ -2269,6 +2654,23 @@ address StubGenerator::generate_disjoint_long_oop_copy(StubId stub_id, address * __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // retrieve the registered handler addresses + address end = __ pc(); + if (add_handlers) { + retrieve_unsafe_access_handlers(start, end, extras); + } + assert(extras.length() == expected_handler_count, + "unexpected handler addresses count %d", extras.length()); +#if INCLUDE_ZGC + // retrieve addresses at which ZGC does colour patching + if (add_relocs) { + retrieve_reloc_addresses(start, end, extras); + } +#endif // INCLUDE_ZGC + // record the stub entry and end plus the no_push entry and any + // extra handler addresses + store_archive_data(stub_id, start, end, entries_ptr, extras_ptr); + return start; } @@ -2313,10 +2715,42 @@ address StubGenerator::generate_conjoint_long_oop_copy(StubId stub_id, address n return generate_conjoint_copy_avx3_masked(stub_id, entry, nooverlap_target); } #endif + bool add_handlers = !is_oop && !aligned; + bool add_relocs = UseZGC && is_oop; + bool add_extras = add_handlers || add_relocs; + GrowableArray
entries; + GrowableArray
extras; + int expected_entry_count = (entry != nullptr ? 2 : 1); + int expected_handler_count = (add_handlers ? 2 : 0) * UnsafeMemoryAccess::COLUMN_COUNT; // 0/2 x UMAM {start,end,handler} + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == expected_entry_count, "sanity check"); + GrowableArray
* entries_ptr = (entry_count == 1 ? nullptr : &entries); + GrowableArray
* extras_ptr = (add_extras ? &extras : nullptr); + address start = load_archive_data(stub_id, entries_ptr, extras_ptr); + if (start != nullptr) { + assert(entries.length() == expected_entry_count - 1, + "unexpected entry count %d", entries.length()); + assert(!add_handlers || extras.length() == expected_handler_count, + "unexpected handler addresses count %d", extras.length()); + if (entry != nullptr) { + *entry = entries.at(0); + } + if (add_handlers) { + // restore 2 UMAM {start,end,handler} addresses from extras + register_unsafe_access_handlers(extras, 0, 2); + } +#if INCLUDE_ZGC + // register addresses at which ZGC does colour patching + if (add_relocs) { + register_reloc_addresses(extras, 0, extras.length()); + } +#endif // INCLUDE_ZGC + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label L_copy_bytes, L_copy_8_bytes, L_exit; const Register from = rdi; // source array address @@ -2329,6 +2763,7 @@ address StubGenerator::generate_conjoint_long_oop_copy(StubId stub_id, address n if (entry != nullptr) { *entry = __ pc(); + entries.append(*entry); // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) BLOCK_COMMENT("Entry:"); } @@ -2350,7 +2785,7 @@ address StubGenerator::generate_conjoint_long_oop_copy(StubId stub_id, address n bs->arraycopy_prologue(_masm, decorators, type, from, to, qword_count); { // UnsafeMemoryAccess page error: continue after unsafe access - UnsafeMemoryAccessMark umam(this, !is_oop && !aligned, true); + UnsafeMemoryAccessMark umam(this, add_handlers, true); __ jmp(L_copy_bytes); @@ -2377,7 +2812,7 @@ address StubGenerator::generate_conjoint_long_oop_copy(StubId stub_id, address n } { // UnsafeMemoryAccess page error: continue after unsafe access - UnsafeMemoryAccessMark umam(this, !is_oop && !aligned, true); + UnsafeMemoryAccessMark umam(this, add_handlers, true); // Copy in multi-bytes chunks copy_bytes_backward(from, to, qword_count, rax, r10, L_copy_bytes, L_copy_8_bytes, decorators, is_oop ? T_OBJECT : T_LONG); @@ -2393,6 +2828,24 @@ address StubGenerator::generate_conjoint_long_oop_copy(StubId stub_id, address n __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + + // retrieve the registered handler addresses + address end = __ pc(); + if (add_handlers) { + retrieve_unsafe_access_handlers(start, end, extras); + } + assert(extras.length() == expected_handler_count, + "unexpected handler addresses count %d", extras.length()); +#if INCLUDE_ZGC + // retrieve addresses at which ZGC does colour patching + if ((UseZGC && is_oop)) { + retrieve_reloc_addresses(start, end, extras); + } +#endif // INCLUDE_ZGC + // record the stub entry and end plus the no_push entry and any + // extra handler addresses + store_archive_data(stub_id, start, end, entries_ptr, extras_ptr); + return start; } @@ -2448,6 +2901,28 @@ address StubGenerator::generate_checkcast_copy(StubId stub_id, address *entry) { ShouldNotReachHere(); } + GrowableArray
entries; + GrowableArray
extras; + int expected_entry_count = (entry != nullptr ? 2 : 1); + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == expected_entry_count, "sanity check"); + GrowableArray
* entries_ptr = (entry_count == 1 ? nullptr : &entries); + GrowableArray
* extras_ptr = (UseZGC ? &extras : nullptr); + address start = load_archive_data(stub_id, entries_ptr, extras_ptr); + if (start != nullptr) { + assert(entries.length() == expected_entry_count - 1, + "unexpected addresses count %d", entries.length()); + if (entry != nullptr) { + *entry = entries.at(0); + } +#if INCLUDE_ZGC + if (UseZGC) { + register_reloc_addresses(extras, 0, extras.length()); + } +#endif // INCLUDE_ZGC + return start; + } + Label L_load_element, L_store_element, L_do_card_marks, L_done; // Input registers (after setup_arg_regs) @@ -2477,7 +2952,7 @@ address StubGenerator::generate_checkcast_copy(StubId stub_id, address *entry) { __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); // required for proper stackwalking of RuntimeStub frame @@ -2502,6 +2977,7 @@ address StubGenerator::generate_checkcast_copy(StubId stub_id, address *entry) { // Caller of this entry point must set up the argument registers. if (entry != nullptr) { *entry = __ pc(); + entries.append(*entry); BLOCK_COMMENT("Entry:"); } @@ -2636,6 +3112,16 @@ address StubGenerator::generate_checkcast_copy(StubId stub_id, address *entry) { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + address end = __ pc(); +#if INCLUDE_ZGC + // retrieve addresses at which ZGC does colour patching + if (UseZGC) { + retrieve_reloc_addresses(start, end, extras); + } +#endif // INCLUDE_ZGC + // record the stub entry and end plus the no_push entry + store_archive_data(stub_id, start, end, entries_ptr, extras_ptr); + return start; } @@ -2655,6 +3141,14 @@ address StubGenerator::generate_checkcast_copy(StubId stub_id, address *entry) { address StubGenerator::generate_unsafe_copy(address byte_copy_entry, address short_copy_entry, address int_copy_entry, address long_copy_entry) { + StubId stub_id = StubId::stubgen_unsafe_arraycopy_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + Label L_long_aligned, L_int_aligned, L_short_aligned; // Input registers (before setup_arg_regs) @@ -2666,9 +3160,8 @@ address StubGenerator::generate_unsafe_copy(address byte_copy_entry, address sho const Register bits = rax; // test copy of low bits __ align(CodeEntryAlignment); - StubId stub_id = StubId::stubgen_unsafe_arraycopy_id; StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); // required for proper stackwalking of RuntimeStub frame @@ -2700,6 +3193,9 @@ address StubGenerator::generate_unsafe_copy(address byte_copy_entry, address sho __ shrptr(size, LogBytesPerLong); // size => qword_count __ jump(RuntimeAddress(long_copy_entry)); + // record the stub entry and end plus + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -2801,10 +3297,23 @@ static void do_setmemory_atomic_loop(USM_TYPE type, Register dest, // to an int, short, or byte fill loop. // address StubGenerator::generate_unsafe_setmemory(address unsafe_byte_fill) { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_unsafe_setmemory_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + // we expect three set of extra unsafememory access handler entries + GrowableArray
extras; + int expected_handler_count = 3 * UnsafeMemoryAccess::COLUMN_COUNT; + address start = load_archive_data(stub_id, nullptr, &extras); + if (start != nullptr) { + assert(extras.length() == expected_handler_count, + "unexpected handler addresses count %d", extras.length()); + register_unsafe_access_handlers(extras, 0, 3); + return start; + } + + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); // required for proper stackwalking of RuntimeStub frame assert(unsafe_byte_fill != nullptr, "Invalid call"); @@ -2894,6 +3403,16 @@ address StubGenerator::generate_unsafe_setmemory(address unsafe_byte_fill) { __ jump(RuntimeAddress(unsafe_byte_fill)); } + // retrieve the registered handler addresses + address end = __ pc(); + retrieve_unsafe_access_handlers(start, end, extras); + assert(extras.length() == expected_handler_count, + "unexpected handler addresses count %d", extras.length()); + + // record the stub entry and end plus the no_push entry and any + // extra handler addresses + store_archive_data(stub_id, start, end, nullptr, &extras); + return start; } @@ -2950,7 +3469,15 @@ address StubGenerator::generate_generic_copy(address byte_copy_entry, address sh address int_copy_entry, address oop_copy_entry, address long_copy_entry, address checkcast_copy_entry) { - Label L_failed, L_failed_0, L_objArray; + StubId stub_id = StubId::stubgen_generic_arraycopy_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + + Label L_failed, L_failed_0, L_skip_failed_0, L_objArray; Label L_copy_shorts, L_copy_ints, L_copy_longs; // Input registers @@ -2966,22 +3493,9 @@ address StubGenerator::generate_generic_copy(address byte_copy_entry, address sh const Register rklass_tmp = rdi; // load_klass #endif - { int modulus = CodeEntryAlignment; - int target = modulus - 5; // 5 = sizeof jmp(L_failed) - int advance = target - (__ offset() % modulus); - if (advance < 0) advance += modulus; - if (advance > 0) __ nop(advance); - } - StubId stub_id = StubId::stubgen_generic_arraycopy_id; StubCodeMark mark(this, stub_id); - - // Short-hop target to L_failed. Makes for denser prologue code. - __ BIND(L_failed_0); - __ jmp(L_failed); - assert(__ offset() % CodeEntryAlignment == 0, "no further alignment needed"); - __ align(CodeEntryAlignment); - address start = __ pc(); + start = __ pc(); __ enter(); // required for proper stackwalking of RuntimeStub frame @@ -3022,7 +3536,8 @@ address StubGenerator::generate_generic_copy(address byte_copy_entry, address sh // if (dst_pos < 0) return -1; __ testl(dst_pos, dst_pos); // dst_pos (32-bits) size_t j4off = __ offset(); - __ jccb(Assembler::negative, L_failed_0); + // skip over the failure trampoline + __ jccb(Assembler::positive, L_skip_failed_0); // The first four tests are very dense code, // but not quite dense enough to put four @@ -3032,6 +3547,13 @@ address StubGenerator::generate_generic_copy(address byte_copy_entry, address sh // Make sure of this. guarantee(((j1off ^ j4off) & ~15) != 0, "I$ line of 1st & 4th jumps"); + // Short-hop target to L_failed. Makes for denser prologue code. + __ BIND(L_failed_0); + __ jmp(L_failed); + + // continue here if first 4 checks pass + __ bind(L_skip_failed_0); + // registers used as temp const Register r11_length = r11; // elements count to copy const Register r10_src_klass = r10; // array klass @@ -3254,6 +3776,9 @@ __ BIND(L_failed); __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_cbrt.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_cbrt.cpp index 73330dedc0f..4c647b7d9dc 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_cbrt.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_cbrt.cpp @@ -191,8 +191,14 @@ ATTRIBUTE_ALIGNED(4) static const juint _D_table[] = address StubGenerator::generate_libmCbrt() { StubId stub_id = StubId::stubgen_dcbrt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label L_2TAG_PACKET_0_0_1, L_2TAG_PACKET_1_0_1, L_2TAG_PACKET_2_0_1; Label B1_1, B1_2, B1_4; @@ -335,7 +341,34 @@ address StubGenerator::generate_libmCbrt() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } #undef __ + +#if INCLUDE_CDS +void StubGenerator::init_AOTAddressTable_cbrt(GrowableArray
& external_addresses) { +#define ADD(addr) external_addresses.append((address)(addr)) + ADD(_ABS_MASK); + ADD(_SIG_MASK); + ADD(_EXP_MASK); + ADD(_EXP_MSK2); + ADD(_EXP_MSK3); + ADD(_SCALE63); + ADD(_ZERON); + ADD(_INF); + ADD(_NEG_INF); + address coeff_table = (address)_coeff_table; + ADD(coeff_table); + ADD(coeff_table + 16); + ADD(coeff_table + 32); + ADD(coeff_table + 48); + ADD(_rcp_table); + ADD(_cbrt_table); + ADD(_D_table); +#undef ADD +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_chacha.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_chacha.cpp index 7afaf34e031..1fa51cd2f18 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_chacha.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_chacha.cpp @@ -111,10 +111,16 @@ void StubGenerator::generate_chacha_stubs() { /* The 2-block AVX/AVX2-enabled ChaCha20 block function implementation */ address StubGenerator::generate_chacha20Block_avx() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_chacha20Block_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label L_twoRounds; const Register state = c_rarg0; @@ -295,15 +301,25 @@ address StubGenerator::generate_chacha20Block_avx() { } __ leave(); __ ret(0); + + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } /* The 4-block AVX512-enabled ChaCha20 block function implementation */ address StubGenerator::generate_chacha20Block_avx512() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_chacha20Block_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label L_twoRounds; const Register state = c_rarg0; @@ -466,6 +482,10 @@ address StubGenerator::generate_chacha20Block_avx512() { __ vzeroupper(); __ leave(); __ ret(0); + + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -584,3 +604,13 @@ bVec, } #undef __ + +#if INCLUDE_CDS +void StubGenerator::init_AOTAddressTable_chacha(GrowableArray
& external_addresses) { +#define ADD(addr) external_addresses.append((address)(addr)) + ADD(CC20_COUNTER_ADD_AVX); + ADD(CC20_COUNTER_ADD_AVX512); + ADD(CC20_LROT_CONSTS); +#undef ADD +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_constants.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_constants.cpp index 93fa7e650db..19e1ca680b3 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_constants.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_constants.cpp @@ -233,3 +233,30 @@ ATTRIBUTE_ALIGNED(16) static const juint _Ctable[] = { }; address StubGenerator::Ctable = (address)_Ctable; +#if INCLUDE_CDS +void StubGenerator::init_AOTAddressTable_constants(GrowableArray
& external_addresses) { +#define ADD(addr) external_addresses.append((address)(addr)) + ADD(_ONE); + ADD(_ONEHALF); + ADD(_SIGN_MASK); + ADD(_TWO_POW_55); + ADD(_TWO_POW_M55); + ADD(_SHIFTER); + ADD(_ZERO); + ADD(_SC_1); + ADD(_SC_2); + ADD(_SC_3); + ADD(_SC_4); + // Use value which was already cast to (address): StubGenerator::PI_4; + ADD(PI_4); + ADD(PI_4 + 8); + ADD(_PI32INV); + ADD(_NEG_ZERO); + ADD(_P_1); + ADD(_P_2); + ADD(_P_3); + ADD(_PI_INV_TABLE); + ADD(_Ctable); +#undef ADD +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_cos.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_cos.cpp index 8cb6ead21fd..8dedd50cd97 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_cos.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_cos.cpp @@ -174,8 +174,14 @@ address StubGenerator::generate_libmCos() { StubId stub_id = StubId::stubgen_dcos_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label L_2TAG_PACKET_0_0_1, L_2TAG_PACKET_1_0_1, L_2TAG_PACKET_2_0_1, L_2TAG_PACKET_3_0_1; Label L_2TAG_PACKET_4_0_1, L_2TAG_PACKET_5_0_1, L_2TAG_PACKET_6_0_1, L_2TAG_PACKET_7_0_1; @@ -619,6 +625,9 @@ address StubGenerator::generate_libmCos() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_dilithium.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_dilithium.cpp index b9590939468..de8f52a3c05 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_dilithium.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_dilithium.cpp @@ -401,10 +401,16 @@ static void storeXmms(Register destination, int offset, const XMMRegister xmmReg // static address generate_dilithiumAlmostNtt_avx(StubGenerator *stubgen, int vector_len, MacroAssembler *_masm) { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_dilithiumAlmostNtt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = stubgen->load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(stubgen, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); const Register coeffs = c_rarg0; @@ -646,6 +652,9 @@ static address generate_dilithiumAlmostNtt_avx(StubGenerator *stubgen, __ mov64(rax, 0); // return 0 __ ret(0); + // record the stub entry and end + stubgen->store_archive_data(stub_id, start, __ pc()); + return start; } @@ -657,10 +666,16 @@ static address generate_dilithiumAlmostNtt_avx(StubGenerator *stubgen, // zetas (int[128*8]) = c_rarg1 static address generate_dilithiumAlmostInverseNtt_avx(StubGenerator *stubgen, int vector_len, MacroAssembler *_masm) { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_dilithiumAlmostInverseNtt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = stubgen->load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(stubgen, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); const Register coeffs = c_rarg0; @@ -886,6 +901,9 @@ static address generate_dilithiumAlmostInverseNtt_avx(StubGenerator *stubgen, __ mov64(rax, 0); // return 0 __ ret(0); + // record the stub entry and end + stubgen->store_archive_data(stub_id, start, __ pc()); + return start; } @@ -900,10 +918,16 @@ static address generate_dilithiumAlmostInverseNtt_avx(StubGenerator *stubgen, static address generate_dilithiumNttMult_avx(StubGenerator *stubgen, int vector_len, MacroAssembler *_masm) { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_dilithiumNttMult_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = stubgen->load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(stubgen, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); Label L_loop; @@ -972,6 +996,9 @@ static address generate_dilithiumNttMult_avx(StubGenerator *stubgen, __ mov64(rax, 0); // return 0 __ ret(0); + // record the stub entry and end + stubgen->store_archive_data(stub_id, start, __ pc()); + return start; } @@ -984,10 +1011,16 @@ static address generate_dilithiumNttMult_avx(StubGenerator *stubgen, static address generate_dilithiumMontMulByConstant_avx(StubGenerator *stubgen, int vector_len, MacroAssembler *_masm) { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_dilithiumMontMulByConstant_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = stubgen->load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(stubgen, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); Label L_loop; @@ -1059,6 +1092,9 @@ static address generate_dilithiumMontMulByConstant_avx(StubGenerator *stubgen, __ mov64(rax, 0); // return 0 __ ret(0); + // record the stub entry and end + stubgen->store_archive_data(stub_id, start, __ pc()); + return start; } @@ -1073,10 +1109,16 @@ static address generate_dilithiumMontMulByConstant_avx(StubGenerator *stubgen, // multiplier (int) = c_rarg4 static address generate_dilithiumDecomposePoly_avx(StubGenerator *stubgen, int vector_len, MacroAssembler *_masm) { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_dilithiumDecomposePoly_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = stubgen->load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(stubgen, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); Label L_loop; @@ -1318,6 +1360,9 @@ static address generate_dilithiumDecomposePoly_avx(StubGenerator *stubgen, __ mov64(rax, 0); // return 0 __ ret(0); + // record the stub entry and end + stubgen->store_archive_data(stub_id, start, __ pc()); + return start; } @@ -1340,3 +1385,21 @@ void StubGenerator::generate_dilithium_stubs() { generate_dilithiumDecomposePoly_avx(this, vector_len, _masm); } } + +#if INCLUDE_CDS +void StubGenerator::init_AOTAddressTable_dilithium(GrowableArray
& external_addresses) { +#define ADD(addr) external_addresses.append((address)(addr)) + // use accessors to correctly identify the relevant addresses + ADD(unshufflePermsAddr(0)); + ADD(unshufflePermsAddr(1)); + ADD(unshufflePermsAddr(2)); + ADD(unshufflePermsAddr(3)); + ADD(unshufflePermsAddr(4)); + ADD(unshufflePermsAddr(5)); + ADD(dilithiumAvx512ConstsAddr(montQInvModRIdx)); + ADD(dilithiumAvx512ConstsAddr(dilithium_qIdx)); + ADD(dilithiumAvx512ConstsAddr(montRSquareModQIdx)); + ADD(dilithiumAvx512ConstsAddr(barrettAddendIdx)); +#undef ADD +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_exp.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_exp.cpp index 5130fd2c9d2..3c8babcbecf 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_exp.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_exp.cpp @@ -166,8 +166,14 @@ ATTRIBUTE_ALIGNED(4) static const juint _INF[] = address StubGenerator::generate_libmExp() { StubId stub_id = StubId::stubgen_dexp_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label L_2TAG_PACKET_0_0_2, L_2TAG_PACKET_1_0_2, L_2TAG_PACKET_2_0_2, L_2TAG_PACKET_3_0_2; Label L_2TAG_PACKET_4_0_2, L_2TAG_PACKET_5_0_2, L_2TAG_PACKET_6_0_2, L_2TAG_PACKET_7_0_2; @@ -381,7 +387,32 @@ address StubGenerator::generate_libmExp() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } #undef __ + +#if INCLUDE_CDS +void StubGenerator::init_AOTAddressTable_exp(GrowableArray
& external_addresses) { +#define ADD(addr) external_addresses.append((address)(addr)); + address cv = (address)_cv; + ADD(cv); + ADD(cv + 16); + ADD(cv + 32); + ADD(cv + 48); + ADD(cv + 64); + ADD(cv + 80); + ADD(_mmask); + ADD(_bias); + ADD(_Tbl_addr); + ADD(_ALLONES); + ADD(_ebias); + ADD(_XMAX); + ADD(_XMIN); + ADD(_INF); +#undef ADD +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_fmod.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_fmod.cpp index b1eaa4b8031..f53985a13b7 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_fmod.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_fmod.cpp @@ -72,13 +72,19 @@ ATTRIBUTE_ALIGNED(32) static const uint64_t CONST_e307[] = { }; address StubGenerator::generate_libmFmod() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_fmod_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); // required for proper stackwalking of RuntimeStub frame - if (VM_Version::supports_avx512vlbwdq()) { // AVX512 version + if (VM_Version::supports_avx512vlbwdq() && VM_Version::supports_fma()) { // AVX512 version // Source used to generate the AVX512 fmod assembly below: // @@ -521,7 +527,22 @@ address StubGenerator::generate_libmFmod() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } #undef __ + +#if INCLUDE_CDS +void StubGenerator::init_AOTAddressTable_fmod(GrowableArray
& external_addresses) { +#define ADD(addr) external_addresses.append((address)(addr)); + ADD(CONST_NaN); + ADD(CONST_1p260); + ADD(CONST_MAX); + ADD(CONST_INF); + ADD(CONST_e307); +#undef ADD +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_ghash.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_ghash.cpp index 6f05b1ab5e6..9ebab07589e 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_ghash.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_ghash.cpp @@ -80,11 +80,17 @@ void StubGenerator::generate_ghash_stubs() { // Single and multi-block ghash operations. address StubGenerator::generate_ghash_processBlocks() { - __ align(CodeEntryAlignment); - Label L_ghash_loop, L_exit; StubId stub_id = StubId::stubgen_ghash_processBlocks_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + Label L_ghash_loop, L_exit; + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); const Register state = c_rarg0; const Register subkeyH = c_rarg1; @@ -211,17 +217,25 @@ address StubGenerator::generate_ghash_processBlocks() { __ leave(); __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } // Ghash single and multi block operations using AVX instructions address StubGenerator::generate_avx_ghash_processBlocks() { - __ align(CodeEntryAlignment); - StubId stub_id = StubId::stubgen_ghash_processBlocks_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); // arguments const Register state = c_rarg0; @@ -237,6 +251,9 @@ address StubGenerator::generate_avx_ghash_processBlocks() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -538,3 +555,14 @@ void StubGenerator::generateHtbl_eight_blocks(Register htbl) { } #undef __ + +#if INCLUDE_CDS +void StubGenerator::init_AOTAddressTable_ghash(GrowableArray
& external_addresses) { +#define ADD(addr) external_addresses.append((address)(addr)); + ADD(GHASH_SHUFFLE_MASK); + ADD(GHASH_LONG_SWAP_MASK); + ADD(GHASH_BYTE_SWAP_MASK); + ADD(GHASH_POLYNOMIAL); +#undef ADD +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_kyber.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_kyber.cpp index 7d5dee6a5df..347a9b936a8 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_kyber.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_kyber.cpp @@ -400,10 +400,16 @@ static int xmm29_29[] = {29, 29, 29, 29}; // ntt_zetas (short[256]) = c_rarg1 address generate_kyberNtt_avx512(StubGenerator *stubgen, MacroAssembler *_masm) { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_kyberNtt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = stubgen->load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(stubgen, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); const Register coeffs = c_rarg0; @@ -487,6 +493,9 @@ address generate_kyberNtt_avx512(StubGenerator *stubgen, __ mov64(rax, 0); // return 0 __ ret(0); + // record the stub entry and end + stubgen->store_archive_data(stub_id, start, __ pc()); + return start; } @@ -496,11 +505,16 @@ address generate_kyberNtt_avx512(StubGenerator *stubgen, // ntt_zetas (short[256]) = c_rarg1 address generate_kyberInverseNtt_avx512(StubGenerator *stubgen, MacroAssembler *_masm) { - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_kyberInverseNtt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = stubgen->load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(stubgen, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); const Register coeffs = c_rarg0; @@ -610,6 +624,9 @@ address generate_kyberInverseNtt_avx512(StubGenerator *stubgen, __ mov64(rax, 0); // return 0 __ ret(0); + // record the stub entry and end + stubgen->store_archive_data(stub_id, start, __ pc()); + return start; } @@ -621,11 +638,16 @@ address generate_kyberInverseNtt_avx512(StubGenerator *stubgen, // zetas (short[128]) = c_rarg3 address generate_kyberNttMult_avx512(StubGenerator *stubgen, MacroAssembler *_masm) { - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_kyberNttMult_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = stubgen->load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(stubgen, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); const Register result = c_rarg0; @@ -731,6 +753,9 @@ address generate_kyberNttMult_avx512(StubGenerator *stubgen, __ mov64(rax, 0); // return 0 __ ret(0); + // record the stub entry and end + stubgen->store_archive_data(stub_id, start, __ pc()); + return start; } @@ -741,11 +766,16 @@ address generate_kyberNttMult_avx512(StubGenerator *stubgen, // b (short[256]) = c_rarg2 address generate_kyberAddPoly_2_avx512(StubGenerator *stubgen, MacroAssembler *_masm) { - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_kyberAddPoly_2_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = stubgen->load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(stubgen, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); const Register result = c_rarg0; @@ -776,6 +806,9 @@ address generate_kyberAddPoly_2_avx512(StubGenerator *stubgen, __ mov64(rax, 0); // return 0 __ ret(0); + // record the stub entry and end + stubgen->store_archive_data(stub_id, start, __ pc()); + return start; } @@ -787,11 +820,16 @@ address generate_kyberAddPoly_2_avx512(StubGenerator *stubgen, // c (short[256]) = c_rarg3 address generate_kyberAddPoly_3_avx512(StubGenerator *stubgen, MacroAssembler *_masm) { - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_kyberAddPoly_3_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = stubgen->load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(stubgen, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); const Register result = c_rarg0; @@ -830,6 +868,9 @@ address generate_kyberAddPoly_3_avx512(StubGenerator *stubgen, __ mov64(rax, 0); // return 0 __ ret(0); + // record the stub entry and end + stubgen->store_archive_data(stub_id, start, __ pc()); + return start; } @@ -841,11 +882,16 @@ address generate_kyberAddPoly_3_avx512(StubGenerator *stubgen, // parsedLength (int) = c_rarg3 address generate_kyber12To16_avx512(StubGenerator *stubgen, MacroAssembler *_masm) { - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_kyber12To16_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = stubgen->load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(stubgen, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); const Register condensed = c_rarg0; @@ -984,6 +1030,9 @@ address generate_kyber12To16_avx512(StubGenerator *stubgen, __ mov64(rax, 0); // return 0 __ ret(0); + // record the stub entry and end + stubgen->store_archive_data(stub_id, start, __ pc()); + return start; } @@ -993,11 +1042,16 @@ address generate_kyber12To16_avx512(StubGenerator *stubgen, // coeffs (short[256]) = c_rarg0 address generate_kyberBarrettReduce_avx512(StubGenerator *stubgen, MacroAssembler *_masm) { - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_kyberBarrettReduce_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = stubgen->load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(stubgen, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); const Register coeffs = c_rarg0; @@ -1021,6 +1075,9 @@ address generate_kyberBarrettReduce_avx512(StubGenerator *stubgen, __ mov64(rax, 0); // return 0 __ ret(0); + // record the stub entry and end + stubgen->store_archive_data(stub_id, start, __ pc()); + return start; } @@ -1038,3 +1095,24 @@ void StubGenerator::generate_kyber_stubs() { } } } + +#if INCLUDE_CDS +void StubGenerator::init_AOTAddressTable_kyber(GrowableArray
& external_addresses) { +#define ADD(addr) external_addresses.append((address)(addr)) + // use accessors to correctly identify the relevant addresses + ADD(kyberAvx512NttPermsAddr()); + ADD(kyberAvx512InverseNttPermsAddr()); + ADD(kyberAvx512_nttMultPermsAddr()); + ADD(kyberAvx512_12To16PermsAddr()); + ADD(kyberAvx512_12To16DupAddr()); + ADD(kyberAvx512_12To16ShiftAddr()); + ADD(kyberAvx512_12To16AndAddr()); + ADD(kyberAvx512ConstsAddr(qOffset)); + ADD(kyberAvx512ConstsAddr(qInvModROffset)); + ADD(kyberAvx512ConstsAddr(dimHalfInverseOffset)); + ADD(kyberAvx512ConstsAddr(barretMultiplierOffset)); + ADD(kyberAvx512ConstsAddr(montRSquareModqOffset)); + ADD(kyberAvx512ConstsAddr(f00Offset)); +#undef ADD +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_log.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_log.cpp index 6b5b4d704e3..07683a51e3d 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_log.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_log.cpp @@ -177,8 +177,14 @@ ATTRIBUTE_ALIGNED(16) static const juint _coeff[] = address StubGenerator::generate_libmLog() { StubId stub_id = StubId::stubgen_dlog_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label L_2TAG_PACKET_0_0_2, L_2TAG_PACKET_1_0_2, L_2TAG_PACKET_2_0_2, L_2TAG_PACKET_3_0_2; Label L_2TAG_PACKET_4_0_2, L_2TAG_PACKET_5_0_2, L_2TAG_PACKET_6_0_2, L_2TAG_PACKET_7_0_2; @@ -359,6 +365,9 @@ address StubGenerator::generate_libmLog() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -516,8 +525,14 @@ ATTRIBUTE_ALIGNED(16) static const juint _coeff_log10[] = address StubGenerator::generate_libmLog10() { StubId stub_id = StubId::stubgen_dlog10_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label L_2TAG_PACKET_0_0_2, L_2TAG_PACKET_1_0_2, L_2TAG_PACKET_2_0_2, L_2TAG_PACKET_3_0_2; Label L_2TAG_PACKET_4_0_2, L_2TAG_PACKET_5_0_2, L_2TAG_PACKET_6_0_2, L_2TAG_PACKET_7_0_2; @@ -704,7 +719,38 @@ address StubGenerator::generate_libmLog10() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } #undef __ + +#if INCLUDE_CDS +void StubGenerator::init_AOTAddressTable_log(GrowableArray
& external_addresses) { +#define ADD(addr) external_addresses.append((address)(addr)); + address log2 = (address)_log2; + address coeff = (address)_coeff; + address LOG10_E = (address)_LOG10_E; + address log2_log10 = (address)_log2_log10; + address coeff_log10 = (address)_coeff_log10; + + ADD(_L_tbl); + ADD(log2); + ADD(log2 + 8); + ADD(coeff); + ADD(coeff + 16); + ADD(coeff + 32); + ADD(_HIGHSIGMASK_log10); + ADD(LOG10_E); + ADD(LOG10_E + 8); + ADD(_L_tbl_log10); + ADD(log2_log10); + ADD(log2_log10 + 8); + ADD(coeff_log10); + ADD(coeff_log10 + 16); + ADD(coeff_log10 + 32); +#undef ADD +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_poly1305.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_poly1305.cpp index c80b2d16181..ea7e6d64254 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_poly1305.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_poly1305.cpp @@ -909,10 +909,16 @@ void StubGenerator::poly1305_process_blocks_avx512( // After execution, input and length will point at remaining (unprocessed) data // and accumulator will point to the current accumulator value address StubGenerator::generate_poly1305_processBlocks() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_poly1305_processBlocks_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); // Save all 'SOE' registers @@ -1028,6 +1034,10 @@ address StubGenerator::generate_poly1305_processBlocks() { __ leave(); __ ret(0); + + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -1695,3 +1705,14 @@ void StubGenerator::poly1305_msg_mul_reduce_vec4_avx2( __ vpaddq(A1, A1, YTMP2, Assembler::AVX_256bit); //Add medium 42-bit bits from new blocks to accumulator __ vpaddq(A1, A1, YTMP5, Assembler::AVX_256bit); } +#undef __ + +#if INCLUDE_CDS +void StubGenerator::init_AOTAddressTable_poly1305(GrowableArray
& external_addresses) { +#define ADD(addr) external_addresses.append((address)(addr)); + ADD(POLY1305_PAD_MSG); + ADD(POLY1305_MASK42); + ADD(POLY1305_MASK44); +#undef ADD +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_poly_mont.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_poly_mont.cpp index c439e0b370f..308a8042993 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_poly_mont.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_poly_mont.cpp @@ -558,10 +558,16 @@ void montgomeryMultiplyAVX2(const Register aLimbs, const Register bLimbs, const } address StubGenerator::generate_intpoly_montgomeryMult_P256() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_intpoly_montgomeryMult_P256_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); if (VM_Version::supports_avx512ifma() && VM_Version::supports_avx512vlbw()) { @@ -620,6 +626,10 @@ address StubGenerator::generate_intpoly_montgomeryMult_P256() { __ leave(); __ ret(0); + + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -680,10 +690,16 @@ address StubGenerator::generate_intpoly_assign() { // P521OrderField: 19 = 8 + 8 + 2 + 1 // Special Cases 5, 10, 14, 16, 19 - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_intpoly_assign_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); // Inputs @@ -762,5 +778,24 @@ address StubGenerator::generate_intpoly_assign() { __ bind(L_Done); __ leave(); __ ret(0); + + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } +#undef __ + +#if INCLUDE_CDS +void StubGenerator::init_AOTAddressTable_poly_mont(GrowableArray
& external_addresses) { +#define ADD(addr) external_addresses.append((address)(addr)); + // use accessors to retrieve all correct addresses + ADD(shift_1L()); + ADD(shift_1R()); + ADD(p256_mask52()); + ADD(mask_limb5()); + ADD(modulus_p256()); + ADD(modulus_p256(1)); +#undef ADD +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_pow.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_pow.cpp index 3c3df7e6ac4..a9a6dc10da4 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_pow.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_pow.cpp @@ -760,8 +760,14 @@ ATTRIBUTE_ALIGNED(8) static const juint _DOUBLE0DOT5[] = { address StubGenerator::generate_libmPow() { StubId stub_id = StubId::stubgen_dpow_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label L_2TAG_PACKET_0_0_2, L_2TAG_PACKET_1_0_2, L_2TAG_PACKET_2_0_2, L_2TAG_PACKET_3_0_2; Label L_2TAG_PACKET_4_0_2, L_2TAG_PACKET_5_0_2, L_2TAG_PACKET_6_0_2, L_2TAG_PACKET_7_0_2; @@ -1859,7 +1865,45 @@ address StubGenerator::generate_libmPow() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } #undef __ + +#if INCLUDE_CDS +void StubGenerator::init_AOTAddressTable_pow(GrowableArray
& external_addresses) { +#define ADD(addr) external_addresses.append((address)(addr)); + address HIGHMASK_Y = (address)_HIGHMASK_Y; + address e_coeff = (address)_e_coeff; + address coeff_h = (address)_coeff_h; + address coeff_pow = (address)_coeff_pow; + + ADD(_HIGHSIGMASK); + ADD(_LOG2_E); + ADD(HIGHMASK_Y); + ADD(HIGHMASK_Y + 8); + ADD(_T_exp); + ADD(e_coeff); + ADD(e_coeff + 16); + ADD(e_coeff + 32); + ADD(coeff_h); + ADD(coeff_h + 8); + ADD(_HIGHMASK_LOG_X); + ADD(_HALFMASK); + ADD(coeff_pow); + ADD(coeff_pow + 16); + ADD(coeff_pow + 32); + ADD(coeff_pow + 48); + ADD(coeff_pow + 64); + ADD(coeff_pow + 80); + ADD(_L_tbl_pow); + ADD(_log2_pow); + ADD(_DOUBLE2); + ADD(_DOUBLE0); + ADD(_DOUBLE0DOT5); +#undef ADD +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_sha3.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_sha3.cpp index f9d876f34f3..58f81652a0c 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_sha3.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_sha3.cpp @@ -104,10 +104,15 @@ static address generate_sha3_implCompress(StubId stub_id, default: ShouldNotReachHere(); } - + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = stubgen->load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(stubgen, stub_id); - address start = __ pc(); + start = __ pc(); const Register buf = c_rarg0; const Register state = c_rarg1; @@ -316,6 +321,9 @@ static address generate_sha3_implCompress(StubId stub_id, __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + stubgen->store_archive_data(stub_id, start, __ pc()); + return start; } @@ -326,10 +334,16 @@ static address generate_sha3_implCompress(StubId stub_id, // Performs two keccak() computations in parallel. The steps of the // two computations are executed interleaved. static address generate_double_keccak(StubGenerator *stubgen, MacroAssembler *_masm) { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_double_keccak_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = stubgen->load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(stubgen, stub_id); - address start = __ pc(); + start = __ pc(); const Register state0 = c_rarg0; const Register state1 = c_rarg1; @@ -495,6 +509,9 @@ static address generate_double_keccak(StubGenerator *stubgen, MacroAssembler *_m __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + stubgen->store_archive_data(stub_id, start, __ pc()); + return start; } @@ -508,3 +525,14 @@ void StubGenerator::generate_sha3_stubs() { generate_sha3_implCompress(StubId::stubgen_sha3_implCompressMB_id, this, _masm); } } + +#undef __ + +#if INCLUDE_CDS +void StubGenerator::init_AOTAddressTable_sha3(GrowableArray
& external_addresses) { +#define ADD(addr) external_addresses.append((address)(addr)); + ADD(round_constsAddr()); + ADD(permsAndRotsAddr()); +#undef ADD +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_sin.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_sin.cpp index 5290e737581..00c759a369b 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_sin.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_sin.cpp @@ -181,8 +181,14 @@ ATTRIBUTE_ALIGNED(8) static const juint _ALL_ONES[] = address StubGenerator::generate_libmSin() { StubId stub_id = StubId::stubgen_dsin_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label L_2TAG_PACKET_0_0_1, L_2TAG_PACKET_1_0_1, L_2TAG_PACKET_2_0_1, L_2TAG_PACKET_3_0_1; Label L_2TAG_PACKET_4_0_1, L_2TAG_PACKET_5_0_1, L_2TAG_PACKET_6_0_1, L_2TAG_PACKET_7_0_1; @@ -645,7 +651,18 @@ address StubGenerator::generate_libmSin() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } #undef __ + +#if INCLUDE_CDS +void StubGenerator::init_AOTAddressTable_sin(GrowableArray
& external_addresses) { +#define ADD(addr) external_addresses.append((address)(addr)); + ADD(_ALL_ONES); +#undef ADD +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_sinh.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_sinh.cpp index 86e4ac20176..9969866cfc7 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_sinh.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_sinh.cpp @@ -290,8 +290,14 @@ ATTRIBUTE_ALIGNED(16) static const juint _T2_neg_f[] = address StubGenerator::generate_libmSinh() { StubId stub_id = StubId::stubgen_dsinh_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label L_2TAG_PACKET_0_0_2, L_2TAG_PACKET_1_0_2, L_2TAG_PACKET_3_0_2, L_2TAG_PACKET_4_0_2; Label L_2TAG_PACKET_5_0_2, L_2TAG_PACKET_6_0_2; @@ -519,7 +525,36 @@ address StubGenerator::generate_libmSinh() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } #undef __ + +#if INCLUDE_CDS +void StubGenerator::init_AOTAddressTable_sinh(GrowableArray
& external_addresses) { +#define ADD(addr) external_addresses.append((address)(addr)); + address L2E = (address)_L2E; + address cv = (address)_cv; + address pv = (address)_pv; + + ADD(L2E); + ADD(L2E + 8); + ADD(_HALFMASK); + ADD(_Shifter); + ADD(cv); + ADD(cv + 16); + ADD(cv + 32); + ADD(cv + 48); + ADD(cv + 64); + ADD(_T2f); + ADD(_T2_neg_f); + ADD(pv); + ADD(pv + 16); + ADD(pv + 32); + ADD(_MASK3); +#undef ADD +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_tan.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_tan.cpp index 4f14414652c..9f91b9e8f84 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_tan.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_tan.cpp @@ -456,8 +456,14 @@ ATTRIBUTE_ALIGNED(8) static const juint _QQ_2_tan[] = address StubGenerator::generate_libmTan() { StubId stub_id = StubId::stubgen_dtan_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label L_2TAG_PACKET_0_0_1, L_2TAG_PACKET_1_0_1, L_2TAG_PACKET_2_0_1, L_2TAG_PACKET_3_0_1; Label L_2TAG_PACKET_4_0_1, L_2TAG_PACKET_5_0_1, L_2TAG_PACKET_6_0_1, L_2TAG_PACKET_7_0_1; @@ -1025,7 +1031,35 @@ address StubGenerator::generate_libmTan() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } #undef __ + +#if INCLUDE_CDS +void StubGenerator::init_AOTAddressTable_tan(GrowableArray
& external_addresses) { +#define ADD(addr) external_addresses.append((address)(addr)); + address PI_4_tan = (address)_PI_4_tan; + + ADD(_MUL16); + ADD(_sign_mask_tan); + ADD(_PI32INV_tan); + ADD(_P_1_tan); + ADD(_P_2_tan); + ADD(_P_3_tan); + ADD(_Ctable_tan); + ADD(_MASK_35_tan); + ADD(_Q_11_tan); + ADD(_Q_9_tan); + ADD(_Q_7_tan); + ADD(_Q_5_tan); + ADD(_Q_3_tan); + ADD(PI_4_tan); + ADD(PI_4_tan + 8); + ADD(_QQ_2_tan); +#undef ADD +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_tanh.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_tanh.cpp index dce4fbfc455..4f2fe8a460b 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_tanh.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_tanh.cpp @@ -303,8 +303,14 @@ ATTRIBUTE_ALIGNED(16) static const juint _T2_neg_f[] = address StubGenerator::generate_libmTanh() { StubId stub_id = StubId::stubgen_dtanh_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label L_2TAG_PACKET_0_0_1, L_2TAG_PACKET_1_0_1, L_2TAG_PACKET_2_0_1, L_2TAG_PACKET_3_0_1; Label L_2TAG_PACKET_4_0_1, L_2TAG_PACKET_5_0_1; @@ -495,7 +501,36 @@ address StubGenerator::generate_libmTanh() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } #undef __ + +#if INCLUDE_CDS +void StubGenerator::init_AOTAddressTable_tanh(GrowableArray
& external_addresses) { +#define ADD(addr) external_addresses.append((address)(addr)); + address L2E = (address)_L2E; + address cv = (address)_cv; + address pv = (address)_pv; + + ADD(L2E); + ADD(L2E + 8); + ADD(_HALFMASK); + ADD(_ONEMASK); + ADD(_TWOMASK); + ADD(_Shifter); + ADD(cv); + ADD(cv + 16); + ADD(cv + 32); + ADD(_T2_neg_f); + ADD(pv); + ADD(pv + 16); + ADD(pv + 32); + ADD(_MASK3); + ADD(_RMASK); +#undef ADD +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/x86/stubRoutines_x86.cpp b/src/hotspot/cpu/x86/stubRoutines_x86.cpp index ee9cea08e64..ce11925dde2 100644 --- a/src/hotspot/cpu/x86/stubRoutines_x86.cpp +++ b/src/hotspot/cpu/x86/stubRoutines_x86.cpp @@ -28,6 +28,10 @@ #include "runtime/stubRoutines.hpp" #include "utilities/globalDefinitions.hpp" #include "crc32c.h" +#include "stubGenerator_x86_64.hpp" +#ifdef COMPILER1 +#include "c1/c1_LIRAssembler.hpp" +#endif // Implementation of the platform-specific part of StubRoutines - for // a description of how to extend it, see the stubRoutines.hpp file. @@ -40,8 +44,12 @@ #define DEFINE_ARCH_ENTRY_INIT(arch, blob_name, stub_name, field_name, getter_name, init_function) \ address StubRoutines:: arch :: STUB_FIELD_NAME(field_name) = CAST_FROM_FN_PTR(address, init_function); -STUBGEN_ARCH_ENTRIES_DO(DEFINE_ARCH_ENTRY, DEFINE_ARCH_ENTRY_INIT) +#define DEFINE_ARCH_ENTRY_ARRAY(arch, blob_name, stub_name, field_name, getter_name, count) \ + address StubRoutines:: arch :: STUB_FIELD_NAME(field_name) [count]; +STUBGEN_ARCH_ENTRIES_DO(DEFINE_ARCH_ENTRY, DEFINE_ARCH_ENTRY_INIT, DEFINE_ARCH_ENTRY_ARRAY) + +#undef DEFINE_ARCH_ENTRY_ARRAY #undef DEFINE_ARCH_ENTRY_INIT #undef DEFINE_ARCH_ENTRY @@ -411,3 +419,46 @@ ATTRIBUTE_ALIGNED(64) const julong StubRoutines::x86::_k512_W[] = 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL, }; + +#if INCLUDE_CDS + +void StubRoutines::init_AOTAddressTable() { + ResourceMark rm; + GrowableArray
external_addresses; + // publish static addresses referred to by main x86 generator and + // auxiliary x86 generators + StubGenerator::init_AOTAddressTable(external_addresses); + // publish external data addresses defined in nested x86 class + StubRoutines::x86::init_AOTAddressTable(external_addresses); +#ifdef COMPILER1 + LIR_Assembler::init_AOTAddressTable(external_addresses); +#endif + AOTCodeCache::publish_external_addresses(external_addresses); +} + +// publish addresses of external data defined in this file which may +// be referenced from stub or code +void StubRoutines::x86::init_AOTAddressTable(GrowableArray
& external_addresses) { +#define ADD(addr) external_addresses.append((address)(addr)); + ADD(&_mxcsr_std); + ADD(&_mxcsr_rz); + ADD(crc_by128_masks_addr()); + ADD(crc_by128_masks_addr() + 16); + ADD(crc_by128_masks_addr() + 32); + // this is added in generic code + // ADD(_crc_table); + ADD(crc_by128_masks_avx512_addr()); + ADD(crc_by128_masks_avx512_addr() + 16); + ADD(crc_by128_masks_avx512_addr() + 32); + ADD(_crc_table_avx512); + ADD(_crc32c_table_avx512); + ADD(_shuf_table_crc32_avx512); + // n.b. call accessor for this one to ensure the table is generated + ADD(crc32c_table_addr()); + ADD(_arrays_hashcode_powers_of_31); + ADD(_k256); + ADD(_k256_W); + ADD(_k512_W); +#undef ADD +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/x86/stubRoutines_x86.hpp b/src/hotspot/cpu/x86/stubRoutines_x86.hpp index 3654b644131..7283798888b 100644 --- a/src/hotspot/cpu/x86/stubRoutines_x86.hpp +++ b/src/hotspot/cpu/x86/stubRoutines_x86.hpp @@ -55,9 +55,13 @@ class x86 { #define DECLARE_ARCH_ENTRY_INIT(arch, blob_name, stub_name, field_name, getter_name, init_function) \ DECLARE_ARCH_ENTRY(arch, blob_name, stub_name, field_name, getter_name) -private: - STUBGEN_ARCH_ENTRIES_DO(DECLARE_ARCH_ENTRY, DECLARE_ARCH_ENTRY_INIT) +#define DECLARE_ARCH_ENTRY_ARRAY(arch, blob_name, stub_name, field_name, getter_name, count) \ + static address STUB_FIELD_NAME(field_name) [count] ; +private: + STUBGEN_ARCH_ENTRIES_DO(DECLARE_ARCH_ENTRY, DECLARE_ARCH_ENTRY_INIT, DECLARE_ARCH_ENTRY_ARRAY) + +#undef DECLARE_ARCH_ENTRY_ARRAY #undef DECLARE_ARCH_ENTRY_INIT #undef DECLARE_ARCH_ENTRY @@ -70,9 +74,13 @@ private: #define DEFINE_ARCH_ENTRY_GETTER_INIT(arch, blob_name, stub_name, field_name, getter_name, init_function) \ DEFINE_ARCH_ENTRY_GETTER(arch, blob_name, stub_name, field_name, getter_name) -public: - STUBGEN_ARCH_ENTRIES_DO(DEFINE_ARCH_ENTRY_GETTER, DEFINE_ARCH_ENTRY_GETTER_INIT) +#define DEFINE_ARCH_ENTRY_GETTER_ARRAY(arch, blob_name, stub_name, field_name, getter_name, count) \ + static address getter_name(int idx) { return STUB_FIELD_NAME(field_name) [idx]; } +public: + STUBGEN_ARCH_ENTRIES_DO(DEFINE_ARCH_ENTRY_GETTER, DEFINE_ARCH_ENTRY_GETTER_INIT, DEFINE_ARCH_ENTRY_GETTER_ARRAY) + +#undef DEFINE_ARCH_ENTRY_GETTER_ARRAY #undef DEFINE_ARCH_ENTRY_GETTER_INIT #undef DEFINE_ARCH_GETTER_ENTRY @@ -112,6 +120,8 @@ public: static address arrays_hashcode_powers_of_31() { return (address)_arrays_hashcode_powers_of_31; } static void generate_CRC32C_table(bool is_pclmulqdq_supported); + + static void init_AOTAddressTable(GrowableArray
& external_addresses); }; #endif // CPU_X86_STUBROUTINES_X86_HPP diff --git a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp index 4301bd328d6..7d9ceb9d446 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.cpp +++ b/src/hotspot/cpu/x86/vm_version_x86.cpp @@ -1094,15 +1094,36 @@ void VM_Version::get_processor_features() { } } - // Currently APX support is only enabled for targets supporting AVX512VL feature. - bool apx_supported = os_supports_apx_egprs() && supports_apx_f() && supports_avx512vl(); - if (UseAPX && !apx_supported) { - warning("UseAPX is not supported on this CPU, setting it to false"); + // Currently APX support is only enabled for targets supporting AVX512VL feature. + if (supports_apx_f() && os_supports_apx_egprs() && supports_avx512vl()) { + if (FLAG_IS_DEFAULT(UseAPX)) { + UseAPX = false; // by default UseAPX is false + _features.clear_feature(CPU_APX_F); + } else if (!UseAPX) { + _features.clear_feature(CPU_APX_F); + } + } else if (UseAPX) { + if (!FLAG_IS_DEFAULT(UseAPX)) { + warning("APX is not supported on this CPU, setting it to false)"); + } FLAG_SET_DEFAULT(UseAPX, false); } - if (!UseAPX) { - _features.clear_feature(CPU_APX_F); + CHECK_CPU_FEATURE(supports_clmul, CLMUL); + CHECK_CPU_FEATURE(supports_aes, AES); + CHECK_CPU_FEATURE(supports_fma, FMA); + + if (supports_sha() || (supports_avx2() && supports_bmi2())) { + if (FLAG_IS_DEFAULT(UseSHA)) { + UseSHA = true; + } else if (!UseSHA) { + _features.clear_feature(CPU_SHA); + } + } else if (UseSHA) { + if (!FLAG_IS_DEFAULT(UseSHA)) { + warning("SHA instructions are not available on this CPU"); + } + FLAG_SET_DEFAULT(UseSHA, false); } if (FLAG_IS_DEFAULT(IntelJccErratumMitigation)) { @@ -1152,10 +1173,50 @@ void VM_Version::get_processor_features() { // Use AES instructions if available. if (supports_aes()) { - if (FLAG_IS_DEFAULT(UseAES)) { - FLAG_SET_DEFAULT(UseAES, true); + if (supports_sse3()) { + if (FLAG_IS_DEFAULT(UseAESIntrinsics)) { + FLAG_SET_DEFAULT(UseAESIntrinsics, true); + } + } else if (UseAESIntrinsics) { + // The AES intrinsic stubs require AES instruction support (of course) + // but also require sse3 mode or higher for instructions it use. + if (!FLAG_IS_DEFAULT(UseAESIntrinsics)) { + warning("X86 AES intrinsics require SSE3 instructions or higher. Intrinsics will be disabled."); + } + FLAG_SET_DEFAULT(UseAESIntrinsics, false); } - if (!UseAES) { + if (!UseAESIntrinsics) { + if (UseAESCTRIntrinsics) { + if (!FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) { + warning("AES-CTR intrinsics require UseAESIntrinsics flag to be enabled. Intrinsics will be disabled."); + } + FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); + } + } else { + if (supports_sse4_1()) { + if (FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) { + FLAG_SET_DEFAULT(UseAESCTRIntrinsics, true); + } + } else if (UseAESCTRIntrinsics) { + // The AES-CTR intrinsic stubs require AES instruction support (of course) + // but also require sse4.1 mode or higher for instructions it use. + if (!FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) { + warning("X86 AES-CTR intrinsics require SSE4.1 instructions or higher. Intrinsics will be disabled."); + } + FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); + } + } + } else { + if (!cpu_supports_aes()) { + if (UseAESIntrinsics && !FLAG_IS_DEFAULT(UseAESIntrinsics)) { + warning("AES intrinsics are not available on this CPU"); + } + FLAG_SET_DEFAULT(UseAESIntrinsics, false); + if (UseAESCTRIntrinsics && !FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) { + warning("AES-CTR intrinsics are not available on this CPU"); + } + FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); + } else if (!UseAES) { if (UseAESIntrinsics && !FLAG_IS_DEFAULT(UseAESIntrinsics)) { warning("AES intrinsics require UseAES flag to be enabled. Intrinsics will be disabled."); } @@ -1164,66 +1225,7 @@ void VM_Version::get_processor_features() { warning("AES_CTR intrinsics require UseAES flag to be enabled. AES_CTR intrinsics will be disabled."); } FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); - } else { - if (UseSSE > 2) { - if (FLAG_IS_DEFAULT(UseAESIntrinsics)) { - FLAG_SET_DEFAULT(UseAESIntrinsics, true); - } - } else { - // The AES intrinsic stubs require AES instruction support (of course) - // but also require sse3 mode or higher for instructions it use. - if (UseAESIntrinsics && !FLAG_IS_DEFAULT(UseAESIntrinsics)) { - warning("X86 AES intrinsics require SSE3 instructions or higher. Intrinsics will be disabled."); - } - FLAG_SET_DEFAULT(UseAESIntrinsics, false); - } - - // --AES-CTR begins-- - if (!UseAESIntrinsics) { - if (UseAESCTRIntrinsics && !FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) { - warning("AES-CTR intrinsics require UseAESIntrinsics flag to be enabled. Intrinsics will be disabled."); - } - FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); - } else { - if (supports_sse4_1()) { - if (FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) { - FLAG_SET_DEFAULT(UseAESCTRIntrinsics, true); - } - } else { - // The AES-CTR intrinsic stubs require AES instruction support (of course) - // but also require sse4.1 mode or higher for instructions it use. - if (UseAESCTRIntrinsics && !FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) { - warning("X86 AES-CTR intrinsics require SSE4.1 instructions or higher. Intrinsics will be disabled."); - } - FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); - } - } - // --AES-CTR ends-- } - } else if (UseAES || UseAESIntrinsics || UseAESCTRIntrinsics) { - if (UseAES && !FLAG_IS_DEFAULT(UseAES)) { - warning("AES instructions are not available on this CPU"); - } - FLAG_SET_DEFAULT(UseAES, false); - if (UseAESIntrinsics && !FLAG_IS_DEFAULT(UseAESIntrinsics)) { - warning("AES intrinsics are not available on this CPU"); - } - FLAG_SET_DEFAULT(UseAESIntrinsics, false); - if (UseAESCTRIntrinsics && !FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) { - warning("AES-CTR intrinsics are not available on this CPU"); - } - FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); - } - - // Use CLMUL instructions if available. - if (supports_clmul()) { - if (FLAG_IS_DEFAULT(UseCLMUL)) { - UseCLMUL = true; - } - } else if (UseCLMUL) { - if (!FLAG_IS_DEFAULT(UseCLMUL)) - warning("CLMUL instructions not available on this CPU (AVX may also be required)"); - FLAG_SET_DEFAULT(UseCLMUL, false); } if (UseCLMUL && (UseSSE > 2)) { @@ -1264,8 +1266,9 @@ void VM_Version::get_processor_features() { UseGHASHIntrinsics = true; } } else if (UseGHASHIntrinsics) { - if (!FLAG_IS_DEFAULT(UseGHASHIntrinsics)) + if (!FLAG_IS_DEFAULT(UseGHASHIntrinsics)) { warning("GHASH intrinsic requires CLMUL and SSE2 instructions on this CPU"); + } FLAG_SET_DEFAULT(UseGHASHIntrinsics, false); } @@ -1275,26 +1278,27 @@ void VM_Version::get_processor_features() { // based on the VM capabilities whether to use an AVX2 or AVX512-enabled // version. if (UseAVX >= 1) { - if (FLAG_IS_DEFAULT(UseChaCha20Intrinsics)) { - UseChaCha20Intrinsics = true; - } + if (FLAG_IS_DEFAULT(UseChaCha20Intrinsics)) { + UseChaCha20Intrinsics = true; + } } else if (UseChaCha20Intrinsics) { - if (!FLAG_IS_DEFAULT(UseChaCha20Intrinsics)) { - warning("ChaCha20 intrinsic requires AVX instructions"); - } - FLAG_SET_DEFAULT(UseChaCha20Intrinsics, false); + if (!FLAG_IS_DEFAULT(UseChaCha20Intrinsics)) { + warning("ChaCha20 intrinsic requires AVX instructions"); + } + FLAG_SET_DEFAULT(UseChaCha20Intrinsics, false); } // Kyber Intrinsics // Currently we only have them for AVX512 if (supports_evex() && supports_avx512bw()) { - if (FLAG_IS_DEFAULT(UseKyberIntrinsics)) { - UseKyberIntrinsics = true; - } - } else - if (UseKyberIntrinsics) { - warning("Intrinsics for ML-KEM are not available on this CPU."); - FLAG_SET_DEFAULT(UseKyberIntrinsics, false); + if (FLAG_IS_DEFAULT(UseKyberIntrinsics)) { + UseKyberIntrinsics = true; + } + } else if (UseKyberIntrinsics) { + if (!FLAG_IS_DEFAULT(UseKyberIntrinsics)) { + warning("Intrinsics for ML-KEM are not available on this CPU."); + } + FLAG_SET_DEFAULT(UseKyberIntrinsics, false); } // Dilithium Intrinsics @@ -1303,8 +1307,10 @@ void VM_Version::get_processor_features() { UseDilithiumIntrinsics = true; } } else if (UseDilithiumIntrinsics) { + if (!FLAG_IS_DEFAULT(UseDilithiumIntrinsics)) { warning("Intrinsics for ML-DSA are not available on this CPU."); - FLAG_SET_DEFAULT(UseDilithiumIntrinsics, false); + } + FLAG_SET_DEFAULT(UseDilithiumIntrinsics, false); } // Base64 Intrinsics (Check the condition for which the intrinsic will be active) @@ -1313,39 +1319,24 @@ void VM_Version::get_processor_features() { UseBASE64Intrinsics = true; } } else if (UseBASE64Intrinsics) { - if (!FLAG_IS_DEFAULT(UseBASE64Intrinsics)) + if (!FLAG_IS_DEFAULT(UseBASE64Intrinsics)) { warning("Base64 intrinsic requires EVEX instructions on this CPU"); - FLAG_SET_DEFAULT(UseBASE64Intrinsics, false); - } - - if (supports_fma()) { - if (FLAG_IS_DEFAULT(UseFMA)) { - UseFMA = true; } - } else if (UseFMA) { - warning("FMA instructions are not available on this CPU"); - FLAG_SET_DEFAULT(UseFMA, false); + FLAG_SET_DEFAULT(UseBASE64Intrinsics, false); } if (FLAG_IS_DEFAULT(UseMD5Intrinsics)) { UseMD5Intrinsics = true; } - if (supports_sha() || (supports_avx2() && supports_bmi2())) { - if (FLAG_IS_DEFAULT(UseSHA)) { - UseSHA = true; - } - } else if (UseSHA) { - warning("SHA instructions are not available on this CPU"); - FLAG_SET_DEFAULT(UseSHA, false); - } - if (supports_sha() && supports_sse4_1() && UseSHA) { if (FLAG_IS_DEFAULT(UseSHA1Intrinsics)) { FLAG_SET_DEFAULT(UseSHA1Intrinsics, true); } } else if (UseSHA1Intrinsics) { - warning("Intrinsics for SHA-1 crypto hash functions not available on this CPU."); + if (!FLAG_IS_DEFAULT(UseSHA1Intrinsics)) { + warning("Intrinsics for SHA-1 crypto hash functions not available on this CPU."); + } FLAG_SET_DEFAULT(UseSHA1Intrinsics, false); } @@ -1354,7 +1345,9 @@ void VM_Version::get_processor_features() { FLAG_SET_DEFAULT(UseSHA256Intrinsics, true); } } else if (UseSHA256Intrinsics) { - warning("Intrinsics for SHA-224 and SHA-256 crypto hash functions not available on this CPU."); + if (!FLAG_IS_DEFAULT(UseSHA256Intrinsics)) { + warning("Intrinsics for SHA-224 and SHA-256 crypto hash functions not available on this CPU."); + } FLAG_SET_DEFAULT(UseSHA256Intrinsics, false); } @@ -1363,7 +1356,9 @@ void VM_Version::get_processor_features() { FLAG_SET_DEFAULT(UseSHA512Intrinsics, true); } } else if (UseSHA512Intrinsics) { - warning("Intrinsics for SHA-384 and SHA-512 crypto hash functions not available on this CPU."); + if (!FLAG_IS_DEFAULT(UseSHA512Intrinsics)) { + warning("Intrinsics for SHA-384 and SHA-512 crypto hash functions not available on this CPU."); + } FLAG_SET_DEFAULT(UseSHA512Intrinsics, false); } @@ -1372,14 +1367,12 @@ void VM_Version::get_processor_features() { FLAG_SET_DEFAULT(UseSHA3Intrinsics, true); } } else if (UseSHA3Intrinsics) { - warning("Intrinsics for SHA3-224, SHA3-256, SHA3-384 and SHA3-512 crypto hash functions not available on this CPU."); + if (!FLAG_IS_DEFAULT(UseSHA3Intrinsics)) { + warning("Intrinsics for SHA3-224, SHA3-256, SHA3-384 and SHA3-512 crypto hash functions not available on this CPU."); + } FLAG_SET_DEFAULT(UseSHA3Intrinsics, false); } - if (!(UseSHA1Intrinsics || UseSHA256Intrinsics || UseSHA512Intrinsics || UseSHA3Intrinsics)) { - FLAG_SET_DEFAULT(UseSHA, false); - } - #if COMPILER2_OR_JVMCI int max_vector_size = 0; if (UseAVX == 0 || !os_supports_avx_vectors()) { @@ -1435,7 +1428,9 @@ void VM_Version::get_processor_features() { FLAG_SET_DEFAULT(UsePoly1305Intrinsics, true); } } else if (UsePoly1305Intrinsics) { - warning("Intrinsics for Poly1305 crypto hash functions not available on this CPU."); + if (!FLAG_IS_DEFAULT(UsePoly1305Intrinsics)) { + warning("Intrinsics for Poly1305 crypto hash functions not available on this CPU."); + } FLAG_SET_DEFAULT(UsePoly1305Intrinsics, false); } @@ -1444,7 +1439,9 @@ void VM_Version::get_processor_features() { FLAG_SET_DEFAULT(UseIntPolyIntrinsics, true); } } else if (UseIntPolyIntrinsics) { - warning("Intrinsics for Polynomial crypto functions not available on this CPU."); + if (!FLAG_IS_DEFAULT(UseIntPolyIntrinsics)) { + warning("Intrinsics for Polynomial crypto functions not available on this CPU."); + } FLAG_SET_DEFAULT(UseIntPolyIntrinsics, false); } @@ -1626,7 +1623,7 @@ void VM_Version::get_processor_features() { } #endif // COMPILER2 - if ((supports_sse4_2() && supports_ht()) || supports_avx()) { // Newest Intel cpus + if (is_intel_modern_cpu()) { // Newest Intel cpus if (FLAG_IS_DEFAULT(UseUnalignedLoadStores)) { UseUnalignedLoadStores = true; // use movdqu on newest Intel cpus } @@ -1694,8 +1691,8 @@ void VM_Version::get_processor_features() { if (FLAG_IS_DEFAULT(UseSSE42Intrinsics)) { FLAG_SET_DEFAULT(UseSSE42Intrinsics, true); } - } else { - if (UseSSE42Intrinsics && !FLAG_IS_DEFAULT(UseSSE42Intrinsics)) { + } else if (UseSSE42Intrinsics) { + if (!FLAG_IS_DEFAULT(UseSSE42Intrinsics)) { warning("SSE4.2 intrinsics require SSE4.2 instructions or higher. Intrinsics will be disabled."); } FLAG_SET_DEFAULT(UseSSE42Intrinsics, false); @@ -1705,15 +1702,17 @@ void VM_Version::get_processor_features() { UseVectorizedMismatchIntrinsic = true; } } else if (UseVectorizedMismatchIntrinsic) { - if (!FLAG_IS_DEFAULT(UseVectorizedMismatchIntrinsic)) + if (!FLAG_IS_DEFAULT(UseVectorizedMismatchIntrinsic)) { warning("vectorizedMismatch intrinsics are not available on this CPU"); + } FLAG_SET_DEFAULT(UseVectorizedMismatchIntrinsic, false); } if (UseAVX >= 2) { FLAG_SET_DEFAULT(UseVectorizedHashCodeIntrinsic, true); } else if (UseVectorizedHashCodeIntrinsic) { - if (!FLAG_IS_DEFAULT(UseVectorizedHashCodeIntrinsic)) + if (!FLAG_IS_DEFAULT(UseVectorizedHashCodeIntrinsic)) { warning("vectorizedHashCode intrinsics are not available on this CPU"); + } FLAG_SET_DEFAULT(UseVectorizedHashCodeIntrinsic, false); } @@ -1723,7 +1722,9 @@ void VM_Version::get_processor_features() { UseCountLeadingZerosInstruction = true; } } else if (UseCountLeadingZerosInstruction) { - warning("lzcnt instruction is not available on this CPU"); + if (!FLAG_IS_DEFAULT(UseCountLeadingZerosInstruction)) { + warning("lzcnt instruction is not available on this CPU"); + } FLAG_SET_DEFAULT(UseCountLeadingZerosInstruction, false); } @@ -1739,7 +1740,9 @@ void VM_Version::get_processor_features() { } } } else if (UseCountTrailingZerosInstruction) { - warning("tzcnt instruction is not available on this CPU"); + if (!FLAG_IS_DEFAULT(UseCountTrailingZerosInstruction)) { + warning("tzcnt instruction is not available on this CPU"); + } FLAG_SET_DEFAULT(UseCountTrailingZerosInstruction, false); } @@ -1750,7 +1753,9 @@ void VM_Version::get_processor_features() { UseBMI1Instructions = true; } } else if (UseBMI1Instructions) { - warning("BMI1 instructions are not available on this CPU (AVX is also required)"); + if (!FLAG_IS_DEFAULT(UseBMI1Instructions)) { + warning("BMI1 instructions are not available on this CPU (AVX is also required)"); + } FLAG_SET_DEFAULT(UseBMI1Instructions, false); } @@ -1759,7 +1764,9 @@ void VM_Version::get_processor_features() { UseBMI2Instructions = true; } } else if (UseBMI2Instructions) { - warning("BMI2 instructions are not available on this CPU (AVX is also required)"); + if (!FLAG_IS_DEFAULT(UseBMI2Instructions)) { + warning("BMI2 instructions are not available on this CPU (AVX is also required)"); + } FLAG_SET_DEFAULT(UseBMI2Instructions, false); } @@ -1769,7 +1776,9 @@ void VM_Version::get_processor_features() { UsePopCountInstruction = true; } } else if (UsePopCountInstruction) { - warning("POPCNT instruction is not available on this CPU"); + if (!FLAG_IS_DEFAULT(UsePopCountInstruction)) { + warning("POPCNT instruction is not available on this CPU"); + } FLAG_SET_DEFAULT(UsePopCountInstruction, false); } @@ -1779,7 +1788,9 @@ void VM_Version::get_processor_features() { UseFastStosb = true; } } else if (UseFastStosb) { - warning("fast-string operations are not available on this CPU"); + if (!FLAG_IS_DEFAULT(UseFastStosb)) { + warning("fast-string operations are not available on this CPU"); + } FLAG_SET_DEFAULT(UseFastStosb, false); } @@ -1805,7 +1816,9 @@ void VM_Version::get_processor_features() { UseXMMForObjInit = true; } } else if (UseXMMForObjInit) { - warning("UseXMMForObjInit requires SSE2 and unaligned load/stores. Feature is switched off."); + if (!FLAG_IS_DEFAULT(UseXMMForObjInit)) { + warning("UseXMMForObjInit requires SSE2 and unaligned load/stores. Feature is switched off."); + } FLAG_SET_DEFAULT(UseXMMForObjInit, false); } @@ -1846,7 +1859,7 @@ void VM_Version::get_processor_features() { if (is_intel() && is_intel_server_family() && supports_sse3()) { if (FLAG_IS_DEFAULT(AllocatePrefetchLines) && - supports_sse4_2() && supports_ht()) { // Nehalem based cpus + is_intel_modern_cpu()) { // Nehalem based cpus FLAG_SET_DEFAULT(AllocatePrefetchLines, 4); } #ifdef COMPILER2 @@ -1885,7 +1898,7 @@ void VM_Version::get_processor_features() { if (FLAG_IS_DEFAULT(ContendedPaddingWidth) && (cache_line_size > ContendedPaddingWidth)) - ContendedPaddingWidth = cache_line_size; + ContendedPaddingWidth = cache_line_size; // This machine allows unaligned memory accesses if (FLAG_IS_DEFAULT(UseUnalignedAccesses)) { @@ -2514,7 +2527,7 @@ const char* VM_Version::cpu_brand_string(void) { } int ret_val = cpu_extended_brand_string(_cpu_brand_string, CPU_EBS_MAX_LENGTH); if (ret_val != OS_OK) { - FREE_C_HEAP_ARRAY(char, _cpu_brand_string); + FREE_C_HEAP_ARRAY(_cpu_brand_string); _cpu_brand_string = nullptr; } } @@ -3279,7 +3292,7 @@ int VM_Version::allocate_prefetch_distance(bool use_watermark_prefetch) { } } else { // Intel if (supports_sse3() && is_intel_server_family()) { - if (supports_sse4_2() && supports_ht()) { // Nehalem based cpus + if (is_intel_modern_cpu()) { // Nehalem based cpus return 192; } else if (use_watermark_prefetch) { // watermark prefetching on Core return 384; diff --git a/src/hotspot/cpu/x86/vm_version_x86.hpp b/src/hotspot/cpu/x86/vm_version_x86.hpp index a42558a8023..f721635a02e 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.hpp +++ b/src/hotspot/cpu/x86/vm_version_x86.hpp @@ -535,6 +535,10 @@ protected: static const char* _features_names[]; + static void clear_feature(Feature_Flag feature) { + _features.clear_feature(feature); + } + static void clear_cpu_features() { _features = VM_Features(); _cpu_features = VM_Features(); @@ -930,6 +934,7 @@ public: // Feature identification not affected by VM flags // static bool cpu_supports_evex() { return _cpu_features.supports_feature(CPU_AVX512F); } + static bool cpu_supports_aes() { return _cpu_features.supports_feature(CPU_AES); } static bool supports_avx512_simd_sort() { if (supports_avx512dq()) { @@ -958,6 +963,12 @@ public: static bool is_intel_darkmont(); + static bool is_intel_modern_cpu() { + precond(is_intel()); // should be called only for intel CPU + // Efficient cores in hybrid CPU may not support hyper-threads. + return (supports_avx() || (supports_sse4_2() && (supports_ht() || supports_hybrid()))); + } + static bool is_intel_tsc_synched_at_init(); static void insert_features_names(VM_Version::VM_Features features, stringStream& ss); diff --git a/src/hotspot/cpu/x86/x86.ad b/src/hotspot/cpu/x86/x86.ad index d3a25786b5f..0467f47a9f5 100644 --- a/src/hotspot/cpu/x86/x86.ad +++ b/src/hotspot/cpu/x86/x86.ad @@ -1708,84 +1708,99 @@ static void emit_cmpfp3(MacroAssembler* masm, Register dst) { __ bind(done); } -// Math.min() # Math.max() -// -------------------------- -// ucomis[s/d] # -// ja -> b # a -// jp -> NaN # NaN -// jb -> a # b -// je # -// |-jz -> a | b # a & b -// | -> a # +enum FP_PREC { + fp_prec_hlf, + fp_prec_flt, + fp_prec_dbl +}; + +static inline void emit_fp_ucom(MacroAssembler* masm, enum FP_PREC pt, + XMMRegister p, XMMRegister q) { + if (pt == fp_prec_hlf) { + __ evucomish(p, q); + } else if (pt == fp_prec_flt) { + __ ucomiss(p, q); + } else { + __ ucomisd(p, q); + } +} + +static inline void movfp(MacroAssembler* masm, enum FP_PREC pt, + XMMRegister dst, XMMRegister src, Register scratch) { + if (pt == fp_prec_hlf) { + __ movhlf(dst, src, scratch); + } else if (pt == fp_prec_flt) { + __ movflt(dst, src); + } else { + __ movdbl(dst, src); + } +} + +// Math.min() # Math.max() +// ----------------------------- +// (v)ucomis[h/s/d] # +// ja -> b # a +// jp -> NaN # NaN +// jb -> a # b +// je # +// |-jz -> a | b # a & b +// | -> a # static void emit_fp_min_max(MacroAssembler* masm, XMMRegister dst, XMMRegister a, XMMRegister b, XMMRegister xmmt, Register rt, - bool min, bool single) { + bool min, enum FP_PREC pt) { Label nan, zero, below, above, done; - if (single) - __ ucomiss(a, b); - else - __ ucomisd(a, b); + emit_fp_ucom(masm, pt, a, b); - if (dst->encoding() != (min ? b : a)->encoding()) + if (dst->encoding() != (min ? b : a)->encoding()) { __ jccb(Assembler::above, above); // CF=0 & ZF=0 - else + } else { __ jccb(Assembler::above, done); + } __ jccb(Assembler::parity, nan); // PF=1 __ jccb(Assembler::below, below); // CF=1 // equal __ vpxor(xmmt, xmmt, xmmt, Assembler::AVX_128bit); - if (single) { - __ ucomiss(a, xmmt); - __ jccb(Assembler::equal, zero); + emit_fp_ucom(masm, pt, a, xmmt); - __ movflt(dst, a); - __ jmp(done); - } - else { - __ ucomisd(a, xmmt); - __ jccb(Assembler::equal, zero); + __ jccb(Assembler::equal, zero); + movfp(masm, pt, dst, a, rt); - __ movdbl(dst, a); - __ jmp(done); - } + __ jmp(done); __ bind(zero); - if (min) + if (min) { __ vpor(dst, a, b, Assembler::AVX_128bit); - else + } else { __ vpand(dst, a, b, Assembler::AVX_128bit); + } __ jmp(done); __ bind(above); - if (single) - __ movflt(dst, min ? b : a); - else - __ movdbl(dst, min ? b : a); + movfp(masm, pt, dst, min ? b : a, rt); __ jmp(done); __ bind(nan); - if (single) { + if (pt == fp_prec_hlf) { + __ movl(rt, 0x00007e00); // Float16.NaN + __ evmovw(dst, rt); + } else if (pt == fp_prec_flt) { __ movl(rt, 0x7fc00000); // Float.NaN __ movdl(dst, rt); - } - else { + } else { __ mov64(rt, 0x7ff8000000000000L); // Double.NaN __ movdq(dst, rt); } __ jmp(done); __ bind(below); - if (single) - __ movflt(dst, min ? a : b); - else - __ movdbl(dst, min ? a : b); + movfp(masm, pt, dst, min ? a : b, rt); __ bind(done); } @@ -2605,13 +2620,8 @@ uint BoxLockNode::size(PhaseRegAlloc *ra_) const #ifndef PRODUCT void MachUEPNode::format(PhaseRegAlloc* ra_, outputStream* st) const { - if (UseCompressedClassPointers) { - st->print_cr("movl rscratch1, [j_rarg0 + oopDesc::klass_offset_in_bytes()]\t# compressed klass"); - st->print_cr("\tcmpl rscratch1, [rax + CompiledICData::speculated_klass_offset()]\t # Inline cache check"); - } else { - st->print_cr("movq rscratch1, [j_rarg0 + oopDesc::klass_offset_in_bytes()]\t# compressed klass"); - st->print_cr("\tcmpq rscratch1, [rax + CompiledICData::speculated_klass_offset()]\t # Inline cache check"); - } + st->print_cr("movl rscratch1, [j_rarg0 + oopDesc::klass_offset_in_bytes()]\t# compressed klass"); + st->print_cr("\tcmpl rscratch1, [rax + CompiledICData::speculated_klass_offset()]\t # Inline cache check"); st->print_cr("\tjne SharedRuntime::_ic_miss_stub"); } #endif @@ -7350,146 +7360,140 @@ instruct loadAOTRCAddress(rRegP dst, immAOTRuntimeConstantsAddress con) ins_pipe(ialu_reg_fat); %} +// min = java.lang.Math.min(float a, float b) // max = java.lang.Math.max(float a, float b) -instruct maxF_reg_avx10_2(regF dst, regF a, regF b) %{ - predicate(VM_Version::supports_avx10_2()); +instruct minmaxF_reg_avx10_2(regF dst, regF a, regF b) +%{ + predicate(VM_Version::supports_avx10_2() && !VLoopReductions::is_reduction(n)); match(Set dst (MaxF a b)); - format %{ "maxF $dst, $a, $b" %} - ins_encode %{ - __ eminmaxss($dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, AVX10_2_MINMAX_MAX_COMPARE_SIGN); - %} - ins_pipe( pipe_slow ); -%} - -// max = java.lang.Math.max(float a, float b) -instruct maxF_reg(legRegF dst, legRegF a, legRegF b, legRegF tmp, legRegF atmp, legRegF btmp) %{ - predicate(!VM_Version::supports_avx10_2() && UseAVX > 0 && !VLoopReductions::is_reduction(n)); - match(Set dst (MaxF a b)); - effect(USE a, USE b, TEMP tmp, TEMP atmp, TEMP btmp); - format %{ "maxF $dst, $a, $b \t! using $tmp, $atmp and $btmp as TEMP" %} - ins_encode %{ - __ vminmax_fp(Op_MaxV, T_FLOAT, $dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $tmp$$XMMRegister, $atmp$$XMMRegister, $btmp$$XMMRegister, Assembler::AVX_128bit); - %} - ins_pipe( pipe_slow ); -%} - -instruct maxF_reduction_reg(legRegF dst, legRegF a, legRegF b, legRegF xtmp, rRegI rtmp, rFlagsReg cr) %{ - predicate(!VM_Version::supports_avx10_2() && UseAVX > 0 && VLoopReductions::is_reduction(n)); - match(Set dst (MaxF a b)); - effect(USE a, USE b, TEMP xtmp, TEMP rtmp, KILL cr); - - format %{ "maxF_reduction $dst, $a, $b \t!using $xtmp and $rtmp as TEMP" %} - ins_encode %{ - emit_fp_min_max(masm, $dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $xtmp$$XMMRegister, $rtmp$$Register, - false /*min*/, true /*single*/); - %} - ins_pipe( pipe_slow ); -%} - -// max = java.lang.Math.max(double a, double b) -instruct maxD_reg_avx10_2(regD dst, regD a, regD b) %{ - predicate(VM_Version::supports_avx10_2()); - match(Set dst (MaxD a b)); - format %{ "maxD $dst, $a, $b" %} - ins_encode %{ - __ eminmaxsd($dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, AVX10_2_MINMAX_MAX_COMPARE_SIGN); - %} - ins_pipe( pipe_slow ); -%} - -// max = java.lang.Math.max(double a, double b) -instruct maxD_reg(legRegD dst, legRegD a, legRegD b, legRegD tmp, legRegD atmp, legRegD btmp) %{ - predicate(!VM_Version::supports_avx10_2() && UseAVX > 0 && !VLoopReductions::is_reduction(n)); - match(Set dst (MaxD a b)); - effect(USE a, USE b, TEMP atmp, TEMP btmp, TEMP tmp); - format %{ "maxD $dst, $a, $b \t! using $tmp, $atmp and $btmp as TEMP" %} - ins_encode %{ - __ vminmax_fp(Op_MaxV, T_DOUBLE, $dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $tmp$$XMMRegister, $atmp$$XMMRegister, $btmp$$XMMRegister, Assembler::AVX_128bit); - %} - ins_pipe( pipe_slow ); -%} - -instruct maxD_reduction_reg(legRegD dst, legRegD a, legRegD b, legRegD xtmp, rRegL rtmp, rFlagsReg cr) %{ - predicate(!VM_Version::supports_avx10_2() && UseAVX > 0 && VLoopReductions::is_reduction(n)); - match(Set dst (MaxD a b)); - effect(USE a, USE b, TEMP xtmp, TEMP rtmp, KILL cr); - - format %{ "maxD_reduction $dst, $a, $b \t! using $xtmp and $rtmp as TEMP" %} - ins_encode %{ - emit_fp_min_max(masm, $dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $xtmp$$XMMRegister, $rtmp$$Register, - false /*min*/, false /*single*/); - %} - ins_pipe( pipe_slow ); -%} - -// max = java.lang.Math.min(float a, float b) -instruct minF_reg_avx10_2(regF dst, regF a, regF b) %{ - predicate(VM_Version::supports_avx10_2()); match(Set dst (MinF a b)); - format %{ "minF $dst, $a, $b" %} + + format %{ "minmaxF $dst, $a, $b" %} ins_encode %{ - __ eminmaxss($dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, AVX10_2_MINMAX_MIN_COMPARE_SIGN); + int opcode = this->ideal_Opcode(); + __ sminmax_fp_avx10_2(opcode, T_FLOAT, $dst$$XMMRegister, k0, $a$$XMMRegister, $b$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +instruct minmaxF_reduction_reg_avx10_2(regF dst, regF a, regF b, regF xtmp, rRegI rtmp, rFlagsReg cr) +%{ + predicate(VM_Version::supports_avx10_2() && VLoopReductions::is_reduction(n)); + match(Set dst (MaxF a b)); + match(Set dst (MinF a b)); + effect(USE a, USE b, TEMP xtmp, TEMP rtmp, KILL cr); + + format %{ "minmaxF_reduction $dst, $a, $b \t! using $xtmp and $rtmp as TEMP" %} + ins_encode %{ + int opcode = this->ideal_Opcode(); + bool min = (opcode == Op_MinF) ? true : false; + emit_fp_min_max(masm, $dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $xtmp$$XMMRegister, $rtmp$$Register, + min, fp_prec_flt /*pt*/); %} ins_pipe( pipe_slow ); %} // min = java.lang.Math.min(float a, float b) -instruct minF_reg(legRegF dst, legRegF a, legRegF b, legRegF tmp, legRegF atmp, legRegF btmp) %{ +// max = java.lang.Math.max(float a, float b) +instruct minmaxF_reg(legRegF dst, legRegF a, legRegF b, legRegF tmp, legRegF atmp, legRegF btmp) +%{ predicate(!VM_Version::supports_avx10_2() && UseAVX > 0 && !VLoopReductions::is_reduction(n)); + match(Set dst (MaxF a b)); match(Set dst (MinF a b)); effect(USE a, USE b, TEMP tmp, TEMP atmp, TEMP btmp); - format %{ "minF $dst, $a, $b \t! using $tmp, $atmp and $btmp as TEMP" %} + + format %{ "minmaxF $dst, $a, $b \t! using $tmp, $atmp and $btmp as TEMP" %} ins_encode %{ - __ vminmax_fp(Op_MinV, T_FLOAT, $dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $tmp$$XMMRegister, $atmp$$XMMRegister, $btmp$$XMMRegister, Assembler::AVX_128bit); + int opcode = this->ideal_Opcode(); + int param_opcode = (opcode == Op_MinF) ? Op_MinV : Op_MaxV; + __ vminmax_fp(param_opcode, T_FLOAT, $dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $tmp$$XMMRegister, + $atmp$$XMMRegister, $btmp$$XMMRegister, Assembler::AVX_128bit); %} ins_pipe( pipe_slow ); %} -instruct minF_reduction_reg(legRegF dst, legRegF a, legRegF b, legRegF xtmp, rRegI rtmp, rFlagsReg cr) %{ +instruct minmaxF_reduction_reg(legRegF dst, legRegF a, legRegF b, legRegF xtmp, rRegI rtmp, rFlagsReg cr) +%{ predicate(!VM_Version::supports_avx10_2() && UseAVX > 0 && VLoopReductions::is_reduction(n)); + match(Set dst (MaxF a b)); match(Set dst (MinF a b)); effect(USE a, USE b, TEMP xtmp, TEMP rtmp, KILL cr); - format %{ "minF_reduction $dst, $a, $b \t! using $xtmp and $rtmp as TEMP" %} + format %{ "minmaxF_reduction $dst, $a, $b \t!using $xtmp and $rtmp as TEMP" %} ins_encode %{ + int opcode = this->ideal_Opcode(); + bool min = (opcode == Op_MinF) ? true : false; emit_fp_min_max(masm, $dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $xtmp$$XMMRegister, $rtmp$$Register, - true /*min*/, true /*single*/); - %} - ins_pipe( pipe_slow ); -%} - -// max = java.lang.Math.min(double a, double b) -instruct minD_reg_avx10_2(regD dst, regD a, regD b) %{ - predicate(VM_Version::supports_avx10_2()); - match(Set dst (MinD a b)); - format %{ "minD $dst, $a, $b" %} - ins_encode %{ - __ eminmaxsd($dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, AVX10_2_MINMAX_MIN_COMPARE_SIGN); + min, fp_prec_flt /*pt*/); %} ins_pipe( pipe_slow ); %} // min = java.lang.Math.min(double a, double b) -instruct minD_reg(legRegD dst, legRegD a, legRegD b, legRegD tmp, legRegD atmp, legRegD btmp) %{ - predicate(!VM_Version::supports_avx10_2() && UseAVX > 0 && !VLoopReductions::is_reduction(n)); +// max = java.lang.Math.max(double a, double b) +instruct minmaxD_reg_avx10_2(regD dst, regD a, regD b) +%{ + predicate(VM_Version::supports_avx10_2() && !VLoopReductions::is_reduction(n)); + match(Set dst (MaxD a b)); match(Set dst (MinD a b)); - effect(USE a, USE b, TEMP tmp, TEMP atmp, TEMP btmp); - format %{ "minD $dst, $a, $b \t! using $tmp, $atmp and $btmp as TEMP" %} + + format %{ "minmaxD $dst, $a, $b" %} ins_encode %{ - __ vminmax_fp(Op_MinV, T_DOUBLE, $dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $tmp$$XMMRegister, $atmp$$XMMRegister, $btmp$$XMMRegister, Assembler::AVX_128bit); + int opcode = this->ideal_Opcode(); + __ sminmax_fp_avx10_2(opcode, T_DOUBLE, $dst$$XMMRegister, k0, $a$$XMMRegister, $b$$XMMRegister); %} ins_pipe( pipe_slow ); %} -instruct minD_reduction_reg(legRegD dst, legRegD a, legRegD b, legRegD xtmp, rRegL rtmp, rFlagsReg cr) %{ - predicate(!VM_Version::supports_avx10_2() && UseAVX > 0 && VLoopReductions::is_reduction(n)); +instruct minmaxD_reduction_reg_avx10_2(regD dst, regD a, regD b, regD xtmp, rRegI rtmp, rFlagsReg cr) +%{ + predicate(VM_Version::supports_avx10_2() && VLoopReductions::is_reduction(n)); + match(Set dst (MaxD a b)); match(Set dst (MinD a b)); effect(USE a, USE b, TEMP xtmp, TEMP rtmp, KILL cr); - format %{ "maxD_reduction $dst, $a, $b \t! using $xtmp and $rtmp as TEMP" %} + format %{ "minmaxD_reduction $dst, $a, $b \t! using $xtmp and $rtmp as TEMP" %} ins_encode %{ + int opcode = this->ideal_Opcode(); + bool min = (opcode == Op_MinD) ? true : false; emit_fp_min_max(masm, $dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $xtmp$$XMMRegister, $rtmp$$Register, - true /*min*/, false /*single*/); + min, fp_prec_dbl /*pt*/); + %} + ins_pipe( pipe_slow ); +%} + +// min = java.lang.Math.min(double a, double b) +// max = java.lang.Math.max(double a, double b) +instruct minmaxD_reg(legRegD dst, legRegD a, legRegD b, legRegD tmp, legRegD atmp, legRegD btmp) +%{ + predicate(!VM_Version::supports_avx10_2() && UseAVX > 0 && !VLoopReductions::is_reduction(n)); + match(Set dst (MaxD a b)); + match(Set dst (MinD a b)); + effect(USE a, USE b, TEMP atmp, TEMP btmp, TEMP tmp); + + format %{ "minmaxD $dst, $a, $b \t! using $tmp, $atmp and $btmp as TEMP" %} + ins_encode %{ + int opcode = this->ideal_Opcode(); + int param_opcode = (opcode == Op_MinD) ? Op_MinV : Op_MaxV; + __ vminmax_fp(param_opcode, T_DOUBLE, $dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $tmp$$XMMRegister, + $atmp$$XMMRegister, $btmp$$XMMRegister, Assembler::AVX_128bit); + %} + ins_pipe( pipe_slow ); +%} + +instruct minmaxD_reduction_reg(legRegD dst, legRegD a, legRegD b, legRegD xtmp, rRegL rtmp, rFlagsReg cr) +%{ + predicate(!VM_Version::supports_avx10_2() && UseAVX > 0 && VLoopReductions::is_reduction(n)); + match(Set dst (MaxD a b)); + match(Set dst (MinD a b)); + effect(USE a, USE b, TEMP xtmp, TEMP rtmp, KILL cr); + + format %{ "minmaxD_reduction $dst, $a, $b \t! using $xtmp and $rtmp as TEMP" %} + ins_encode %{ + int opcode = this->ideal_Opcode(); + bool min = (opcode == Op_MinD) ? true : false; + emit_fp_min_max(masm, $dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $xtmp$$XMMRegister, $rtmp$$Register, + min, fp_prec_dbl /*pt*/); %} ins_pipe( pipe_slow ); %} @@ -14399,9 +14403,9 @@ instruct cmpF_cc_regCFE(rFlagsRegUCFE cr, regF src1, regF src2) %{ match(Set cr (CmpF src1 src2)); ins_cost(100); - format %{ "vucomxss $src1, $src2" %} + format %{ "evucomxss $src1, $src2" %} ins_encode %{ - __ vucomxss($src1$$XMMRegister, $src2$$XMMRegister); + __ evucomxss($src1$$XMMRegister, $src2$$XMMRegister); %} ins_pipe(pipe_slow); %} @@ -14421,9 +14425,9 @@ instruct cmpF_cc_memCFE(rFlagsRegUCFE cr, regF src1, memory src2) %{ match(Set cr (CmpF src1 (LoadF src2))); ins_cost(100); - format %{ "vucomxss $src1, $src2" %} + format %{ "evucomxss $src1, $src2" %} ins_encode %{ - __ vucomxss($src1$$XMMRegister, $src2$$Address); + __ evucomxss($src1$$XMMRegister, $src2$$Address); %} ins_pipe(pipe_slow); %} @@ -14443,9 +14447,9 @@ instruct cmpF_cc_immCFE(rFlagsRegUCFE cr, regF src, immF con) %{ match(Set cr (CmpF src con)); ins_cost(100); - format %{ "vucomxss $src, [$constantaddress]\t# load from constant table: float=$con" %} + format %{ "evucomxss $src, [$constantaddress]\t# load from constant table: float=$con" %} ins_encode %{ - __ vucomxss($src$$XMMRegister, $constantaddress($con)); + __ evucomxss($src$$XMMRegister, $constantaddress($con)); %} ins_pipe(pipe_slow); %} @@ -14484,9 +14488,9 @@ instruct cmpD_cc_regCFE(rFlagsRegUCFE cr, regD src1, regD src2) %{ match(Set cr (CmpD src1 src2)); ins_cost(100); - format %{ "vucomxsd $src1, $src2 test" %} + format %{ "evucomxsd $src1, $src2 test" %} ins_encode %{ - __ vucomxsd($src1$$XMMRegister, $src2$$XMMRegister); + __ evucomxsd($src1$$XMMRegister, $src2$$XMMRegister); %} ins_pipe(pipe_slow); %} @@ -14506,9 +14510,9 @@ instruct cmpD_cc_memCFE(rFlagsRegUCFE cr, regD src1, memory src2) %{ match(Set cr (CmpD src1 (LoadD src2))); ins_cost(100); - format %{ "vucomxsd $src1, $src2" %} + format %{ "evucomxsd $src1, $src2" %} ins_encode %{ - __ vucomxsd($src1$$XMMRegister, $src2$$Address); + __ evucomxsd($src1$$XMMRegister, $src2$$Address); %} ins_pipe(pipe_slow); %} @@ -14527,9 +14531,9 @@ instruct cmpD_cc_immCFE(rFlagsRegUCFE cr, regD src, immD con) %{ match(Set cr (CmpD src con)); ins_cost(100); - format %{ "vucomxsd $src, [$constantaddress]\t# load from constant table: double=$con" %} + format %{ "evucomxsd $src, [$constantaddress]\t# load from constant table: double=$con" %} ins_encode %{ - __ vucomxsd($src$$XMMRegister, $constantaddress($con)); + __ evucomxsd($src$$XMMRegister, $constantaddress($con)); %} ins_pipe(pipe_slow); %} @@ -18837,7 +18841,7 @@ instruct ReplHF_reg(vec dst, regF src, rRegI rtmp) %{ format %{ "replicateHF $dst, $src \t! using $rtmp as TEMP" %} ins_encode %{ int vlen_enc = vector_length_encoding(this); - __ vmovw($rtmp$$Register, $src$$XMMRegister); + __ evmovw($rtmp$$Register, $src$$XMMRegister); __ evpbroadcastw($dst$$XMMRegister, $rtmp$$Register, vlen_enc); %} ins_pipe( pipe_slow ); @@ -20952,7 +20956,7 @@ instruct minmaxFP_reg_avx10_2(vec dst, vec a, vec b) %{ int vlen_enc = vector_length_encoding(this); int opcode = this->ideal_Opcode(); BasicType elem_bt = Matcher::vector_element_basic_type(this); - __ vminmax_fp(opcode, elem_bt, $dst$$XMMRegister, k0, $a$$XMMRegister, $b$$XMMRegister, vlen_enc); + __ vminmax_fp_avx10_2(opcode, elem_bt, $dst$$XMMRegister, k0, $a$$XMMRegister, $b$$XMMRegister, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -23968,8 +23972,12 @@ instruct vmask_gen_imm(kReg dst, immL len, rRegL temp) %{ format %{ "vector_mask_gen $len \t! vector mask generator" %} effect(TEMP temp); ins_encode %{ - __ mov64($temp$$Register, (0xFFFFFFFFFFFFFFFFUL >> (64 -$len$$constant))); - __ kmovql($dst$$KRegister, $temp$$Register); + if ($len$$constant > 0) { + __ mov64($temp$$Register, right_n_bits($len$$constant)); + __ kmovql($dst$$KRegister, $temp$$Register); + } else { + __ kxorql($dst$$KRegister, $dst$$KRegister, $dst$$KRegister); + } %} ins_pipe( pipe_slow ); %} @@ -25296,9 +25304,9 @@ instruct vector_selectfrom_twovectors_reg_evex(vec index, vec src1, vec src2) instruct reinterpretS2HF(regF dst, rRegI src) %{ match(Set dst (ReinterpretS2HF src)); - format %{ "vmovw $dst, $src" %} + format %{ "evmovw $dst, $src" %} ins_encode %{ - __ vmovw($dst$$XMMRegister, $src$$Register); + __ evmovw($dst$$XMMRegister, $src$$Register); %} ins_pipe(pipe_slow); %} @@ -25306,9 +25314,9 @@ instruct reinterpretS2HF(regF dst, rRegI src) instruct reinterpretHF2S(rRegI dst, regF src) %{ match(Set dst (ReinterpretHF2S src)); - format %{ "vmovw $dst, $src" %} + format %{ "evmovw $dst, $src" %} ins_encode %{ - __ vmovw($dst$$Register, $src$$XMMRegister); + __ evmovw($dst$$Register, $src$$XMMRegister); %} ins_pipe(pipe_slow); %} @@ -25362,10 +25370,11 @@ instruct scalar_minmax_HF_reg_avx10_2(regF dst, regF src1, regF src2) predicate(VM_Version::supports_avx10_2()); match(Set dst (MaxHF src1 src2)); match(Set dst (MinHF src1 src2)); + format %{ "scalar_min_max_fp16 $dst, $src1, $src2" %} ins_encode %{ - int function = this->ideal_Opcode() == Op_MinHF ? AVX10_2_MINMAX_MIN_COMPARE_SIGN : AVX10_2_MINMAX_MAX_COMPARE_SIGN; - __ eminmaxsh($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, function); + int opcode = this->ideal_Opcode(); + __ sminmax_fp16_avx10_2(opcode, $dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, k0); %} ins_pipe( pipe_slow ); %} @@ -25376,11 +25385,12 @@ instruct scalar_minmax_HF_reg(regF dst, regF src1, regF src2, kReg ktmp, regF xt match(Set dst (MaxHF src1 src2)); match(Set dst (MinHF src1 src2)); effect(TEMP_DEF dst, TEMP ktmp, TEMP xtmp1, TEMP xtmp2); + format %{ "scalar_min_max_fp16 $dst, $src1, $src2\t using $ktmp, $xtmp1 and $xtmp2 as TEMP" %} ins_encode %{ int opcode = this->ideal_Opcode(); - __ scalar_max_min_fp16(opcode, $dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, $ktmp$$KRegister, - $xtmp1$$XMMRegister, $xtmp2$$XMMRegister); + __ sminmax_fp16(opcode, $dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, $ktmp$$KRegister, + $xtmp1$$XMMRegister, $xtmp2$$XMMRegister); %} ins_pipe( pipe_slow ); %} @@ -25480,8 +25490,9 @@ instruct vector_minmax_HF_mem_avx10_2(vec dst, vec src1, memory src2) format %{ "vector_min_max_fp16_mem $dst, $src1, $src2" %} ins_encode %{ int vlen_enc = vector_length_encoding(this); - int function = this->ideal_Opcode() == Op_MinVHF ? AVX10_2_MINMAX_MIN_COMPARE_SIGN : AVX10_2_MINMAX_MAX_COMPARE_SIGN; - __ evminmaxph($dst$$XMMRegister, k0, $src1$$XMMRegister, $src2$$Address, true, function, vlen_enc); + int opcode = this->ideal_Opcode(); + __ vminmax_fp16_avx10_2(opcode, $dst$$XMMRegister, $src1$$XMMRegister, $src2$$Address, + k0, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -25494,8 +25505,9 @@ instruct vector_minmax_HF_reg_avx10_2(vec dst, vec src1, vec src2) format %{ "vector_min_max_fp16 $dst, $src1, $src2" %} ins_encode %{ int vlen_enc = vector_length_encoding(this); - int function = this->ideal_Opcode() == Op_MinVHF ? AVX10_2_MINMAX_MIN_COMPARE_SIGN : AVX10_2_MINMAX_MAX_COMPARE_SIGN; - __ evminmaxph($dst$$XMMRegister, k0, $src1$$XMMRegister, $src2$$XMMRegister, true, function, vlen_enc); + int opcode = this->ideal_Opcode(); + __ vminmax_fp16_avx10_2(opcode, $dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, + k0, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -25510,8 +25522,8 @@ instruct vector_minmax_HF_reg(vec dst, vec src1, vec src2, kReg ktmp, vec xtmp1, ins_encode %{ int vlen_enc = vector_length_encoding(this); int opcode = this->ideal_Opcode(); - __ vector_max_min_fp16(opcode, $dst$$XMMRegister, $src2$$XMMRegister, $src1$$XMMRegister, $ktmp$$KRegister, - $xtmp1$$XMMRegister, $xtmp2$$XMMRegister, vlen_enc); + __ vminmax_fp16(opcode, $dst$$XMMRegister, $src2$$XMMRegister, $src1$$XMMRegister, $ktmp$$KRegister, + $xtmp1$$XMMRegister, $xtmp2$$XMMRegister, vlen_enc); %} ins_pipe( pipe_slow ); %} diff --git a/src/hotspot/cpu/zero/stubDeclarations_zero.hpp b/src/hotspot/cpu/zero/stubDeclarations_zero.hpp index 2357bbb5169..9abe313b3a7 100644 --- a/src/hotspot/cpu/zero/stubDeclarations_zero.hpp +++ b/src/hotspot/cpu/zero/stubDeclarations_zero.hpp @@ -29,35 +29,40 @@ #define STUBGEN_PREUNIVERSE_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(preuniverse, 0) \ #define STUBGEN_INITIAL_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(initial, 0) \ #define STUBGEN_CONTINUATION_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(continuation, 0) \ #define STUBGEN_COMPILER_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(compiler, 0) \ #define STUBGEN_FINAL_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(final, 0) \ diff --git a/src/hotspot/cpu/zero/stubGenerator_zero.cpp b/src/hotspot/cpu/zero/stubGenerator_zero.cpp index 08cb173b507..569a2fa8ca9 100644 --- a/src/hotspot/cpu/zero/stubGenerator_zero.cpp +++ b/src/hotspot/cpu/zero/stubGenerator_zero.cpp @@ -213,7 +213,7 @@ class StubGenerator: public StubCodeGenerator { } public: - StubGenerator(CodeBuffer* code, BlobId blob_id) : StubCodeGenerator(code, blob_id) { + StubGenerator(CodeBuffer* code, BlobId blob_id, AOTStubData *stub_data) : StubCodeGenerator(code, blob_id, stub_data) { switch(blob_id) { case BlobId::stubgen_preuniverse_id: generate_preuniverse_stubs(); @@ -237,8 +237,8 @@ class StubGenerator: public StubCodeGenerator { } }; -void StubGenerator_generate(CodeBuffer* code, BlobId blob_id) { - StubGenerator g(code, blob_id); +void StubGenerator_generate(CodeBuffer* code, BlobId blob_id, AOTStubData *stub_data) { + StubGenerator g(code, blob_id, stub_data); } EntryFrame *EntryFrame::build(const intptr_t* parameters, diff --git a/src/hotspot/cpu/zero/stubRoutines_zero.cpp b/src/hotspot/cpu/zero/stubRoutines_zero.cpp index 9b53f09be5d..196907b061f 100644 --- a/src/hotspot/cpu/zero/stubRoutines_zero.cpp +++ b/src/hotspot/cpu/zero/stubRoutines_zero.cpp @@ -30,3 +30,9 @@ address StubRoutines::crc_table_addr() { ShouldNotCallThis(); return nullptr; } address StubRoutines::crc32c_table_addr() { ShouldNotCallThis(); return nullptr; } + +#if INCLUDE_CDS +// nothing to do for zero +void StubRoutines::init_AOTAddressTable() { +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/os/aix/os_aix.cpp b/src/hotspot/os/aix/os_aix.cpp index 3cad24d388c..32d845b2b6d 100644 --- a/src/hotspot/os/aix/os_aix.cpp +++ b/src/hotspot/os/aix/os_aix.cpp @@ -578,13 +578,13 @@ void os::init_system_properties_values() { char *ld_library_path = NEW_C_HEAP_ARRAY(char, pathsize, mtInternal); os::snprintf_checked(ld_library_path, pathsize, "%s%s" DEFAULT_LIBPATH, v, v_colon); Arguments::set_library_path(ld_library_path); - FREE_C_HEAP_ARRAY(char, ld_library_path); + FREE_C_HEAP_ARRAY(ld_library_path); // Extensions directories. os::snprintf_checked(buf, bufsize, "%s" EXTENSIONS_DIR, Arguments::get_java_home()); Arguments::set_ext_dirs(buf); - FREE_C_HEAP_ARRAY(char, buf); + FREE_C_HEAP_ARRAY(buf); #undef DEFAULT_LIBPATH #undef EXTENSIONS_DIR diff --git a/src/hotspot/os/aix/os_perf_aix.cpp b/src/hotspot/os/aix/os_perf_aix.cpp index cbf78083483..3668ac6ba3f 100644 --- a/src/hotspot/os/aix/os_perf_aix.cpp +++ b/src/hotspot/os/aix/os_perf_aix.cpp @@ -258,10 +258,10 @@ bool CPUPerformanceInterface::CPUPerformance::initialize() { CPUPerformanceInterface::CPUPerformance::~CPUPerformance() { if (_lcpu_names) { - FREE_C_HEAP_ARRAY(perfstat_id_t, _lcpu_names); + FREE_C_HEAP_ARRAY(_lcpu_names); } if (_prev_ticks) { - FREE_C_HEAP_ARRAY(cpu_tick_store_t, _prev_ticks); + FREE_C_HEAP_ARRAY(_prev_ticks); } } @@ -511,12 +511,12 @@ CPUInformationInterface::~CPUInformationInterface() { if (_cpu_info != nullptr) { if (_cpu_info->cpu_name() != nullptr) { const char* cpu_name = _cpu_info->cpu_name(); - FREE_C_HEAP_ARRAY(char, cpu_name); + FREE_C_HEAP_ARRAY(cpu_name); _cpu_info->set_cpu_name(nullptr); } if (_cpu_info->cpu_description() != nullptr) { const char* cpu_desc = _cpu_info->cpu_description(); - FREE_C_HEAP_ARRAY(char, cpu_desc); + FREE_C_HEAP_ARRAY(cpu_desc); _cpu_info->set_cpu_description(nullptr); } delete _cpu_info; @@ -576,7 +576,7 @@ int NetworkPerformanceInterface::NetworkPerformance::network_utilization(Network // check for error if (n_records < 0) { - FREE_C_HEAP_ARRAY(perfstat_netinterface_t, net_stats); + FREE_C_HEAP_ARRAY(net_stats); return OS_ERR; } @@ -593,7 +593,7 @@ int NetworkPerformanceInterface::NetworkPerformance::network_utilization(Network *network_interfaces = new_interface; } - FREE_C_HEAP_ARRAY(perfstat_netinterface_t, net_stats); + FREE_C_HEAP_ARRAY(net_stats); return OS_OK; } diff --git a/src/hotspot/os/aix/porting_aix.cpp b/src/hotspot/os/aix/porting_aix.cpp index b3f878fbfdd..f0527136d90 100644 --- a/src/hotspot/os/aix/porting_aix.cpp +++ b/src/hotspot/os/aix/porting_aix.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2024 SAP SE. All rights reserved. + * Copyright (c) 2012, 2026 SAP SE. All rights reserved. * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -426,6 +426,10 @@ int dladdr(void* addr, Dl_info* info) { } +int JVM_dladdr(void* addr, Dl_info* info) { + return dladdr(addr, info); +} + ///////////////////////////////////////////////////////////////////////////// // Native callstack dumping diff --git a/src/hotspot/os/bsd/os_bsd.cpp b/src/hotspot/os/bsd/os_bsd.cpp index a4d9a2197a5..4c77b619718 100644 --- a/src/hotspot/os/bsd/os_bsd.cpp +++ b/src/hotspot/os/bsd/os_bsd.cpp @@ -444,14 +444,14 @@ void os::init_system_properties_values() { char *ld_library_path = NEW_C_HEAP_ARRAY(char, ld_library_path_size, mtInternal); os::snprintf_checked(ld_library_path, ld_library_path_size, "%s%s" SYS_EXT_DIR "/lib/%s:" DEFAULT_LIBPATH, v, v_colon, cpu_arch); Arguments::set_library_path(ld_library_path); - FREE_C_HEAP_ARRAY(char, ld_library_path); + FREE_C_HEAP_ARRAY(ld_library_path); } // Extensions directories. os::snprintf_checked(buf, bufsize, "%s" EXTENSIONS_DIR ":" SYS_EXT_DIR EXTENSIONS_DIR, Arguments::get_java_home()); Arguments::set_ext_dirs(buf); - FREE_C_HEAP_ARRAY(char, buf); + FREE_C_HEAP_ARRAY(buf); #else // __APPLE__ @@ -538,7 +538,7 @@ void os::init_system_properties_values() { os::snprintf_checked(ld_library_path, ld_library_path_size, "%s%s%s%s%s" SYS_EXTENSIONS_DIR ":" SYS_EXTENSIONS_DIRS ":.", v, v_colon, l, l_colon, user_home_dir); Arguments::set_library_path(ld_library_path); - FREE_C_HEAP_ARRAY(char, ld_library_path); + FREE_C_HEAP_ARRAY(ld_library_path); } // Extensions directories. @@ -550,7 +550,7 @@ void os::init_system_properties_values() { user_home_dir, Arguments::get_java_home()); Arguments::set_ext_dirs(buf); - FREE_C_HEAP_ARRAY(char, buf); + FREE_C_HEAP_ARRAY(buf); #undef SYS_EXTENSIONS_DIR #undef SYS_EXTENSIONS_DIRS diff --git a/src/hotspot/os/bsd/os_perf_bsd.cpp b/src/hotspot/os/bsd/os_perf_bsd.cpp index 78d9519c3a7..47fe3a0d7e9 100644 --- a/src/hotspot/os/bsd/os_perf_bsd.cpp +++ b/src/hotspot/os/bsd/os_perf_bsd.cpp @@ -301,7 +301,7 @@ int SystemProcessInterface::SystemProcesses::system_processes(SystemProcess** sy pids_bytes = proc_listpids(PROC_ALL_PIDS, 0, pids, pids_bytes); if (pids_bytes <= 0) { // couldn't fit buffer, retry. - FREE_RESOURCE_ARRAY(pid_t, pids, pid_count); + FREE_RESOURCE_ARRAY(pids, pid_count); pids = nullptr; try_count++; if (try_count > 3) { @@ -381,12 +381,12 @@ CPUInformationInterface::~CPUInformationInterface() { if (_cpu_info != nullptr) { if (_cpu_info->cpu_name() != nullptr) { const char* cpu_name = _cpu_info->cpu_name(); - FREE_C_HEAP_ARRAY(char, cpu_name); + FREE_C_HEAP_ARRAY(cpu_name); _cpu_info->set_cpu_name(nullptr); } if (_cpu_info->cpu_description() != nullptr) { const char* cpu_desc = _cpu_info->cpu_description(); - FREE_C_HEAP_ARRAY(char, cpu_desc); + FREE_C_HEAP_ARRAY(cpu_desc); _cpu_info->set_cpu_description(nullptr); } delete _cpu_info; diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp index 13a005591fb..4a2d75ecdf3 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp @@ -40,6 +40,8 @@ // Inlined from for portability. #ifndef CGROUP2_SUPER_MAGIC # define CGROUP2_SUPER_MAGIC 0x63677270 +#else + STATIC_ASSERT(CGROUP2_SUPER_MAGIC == 0x63677270); #endif // controller names have to match the *_IDX indices diff --git a/src/hotspot/os/linux/hugepages.cpp b/src/hotspot/os/linux/hugepages.cpp index 1340c470dff..b065f7b1496 100644 --- a/src/hotspot/os/linux/hugepages.cpp +++ b/src/hotspot/os/linux/hugepages.cpp @@ -35,7 +35,7 @@ #include ExplicitHugePageSupport::ExplicitHugePageSupport() : - _initialized{false}, _os_supported{}, _pre_allocated{}, _default_hugepage_size{SIZE_MAX}, _inconsistent{false} {} + _initialized{false}, _os_supported{}, _pre_allocated{}, _default_hugepage_size{0}, _inconsistent{false} {} os::PageSizes ExplicitHugePageSupport::os_supported() const { assert(_initialized, "Not initialized"); @@ -68,7 +68,7 @@ static size_t scan_default_hugepagesize() { // format has been changed), we'll set largest page size to 0 FILE *fp = os::fopen("/proc/meminfo", "r"); - if (fp) { + if (fp != nullptr) { while (!feof(fp)) { int x = 0; char buf[16]; @@ -81,7 +81,7 @@ static size_t scan_default_hugepagesize() { // skip to next line for (;;) { int ch = fgetc(fp); - if (ch == EOF || ch == (int)'\n') break; + if (ch == EOF || ch == '\n') break; } } } @@ -187,7 +187,7 @@ void ExplicitHugePageSupport::scan_os() { } THPSupport::THPSupport() : - _initialized(false), _mode(THPMode::never), _pagesize(SIZE_MAX) {} + _initialized{false}, _mode{THPMode::never}, _pagesize{0} {} THPMode THPSupport::mode() const { @@ -221,7 +221,6 @@ void THPSupport::scan_os() { } // Scan large page size for THP from hpage_pmd_size - _pagesize = 0; if (read_number_file("/sys/kernel/mm/transparent_hugepage/hpage_pmd_size", &_pagesize)) { assert(_pagesize > 0, "Expected"); } diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index c79b0ab9fb5..6927f5108ac 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -710,14 +710,14 @@ void os::init_system_properties_values() { char *ld_library_path = NEW_C_HEAP_ARRAY(char, pathsize, mtInternal); os::snprintf_checked(ld_library_path, pathsize, "%s%s" SYS_EXT_DIR "/lib:" DEFAULT_LIBPATH, v, v_colon); Arguments::set_library_path(ld_library_path); - FREE_C_HEAP_ARRAY(char, ld_library_path); + FREE_C_HEAP_ARRAY(ld_library_path); } // Extensions directories. os::snprintf_checked(buf, bufsize, "%s" EXTENSIONS_DIR ":" SYS_EXT_DIR EXTENSIONS_DIR, Arguments::get_java_home()); Arguments::set_ext_dirs(buf); - FREE_C_HEAP_ARRAY(char, buf); + FREE_C_HEAP_ARRAY(buf); #undef DEFAULT_LIBPATH #undef SYS_EXT_DIR @@ -1313,7 +1313,7 @@ bool os::is_primordial_thread(void) { // Find the virtual memory area that contains addr static bool find_vma(address addr, address* vma_low, address* vma_high) { FILE *fp = os::fopen("/proc/self/maps", "r"); - if (fp) { + if (fp != nullptr) { address low, high; while (!feof(fp)) { if (fscanf(fp, "%p-%p", &low, &high) == 2) { @@ -1326,7 +1326,7 @@ static bool find_vma(address addr, address* vma_low, address* vma_high) { } for (;;) { int ch = fgetc(fp); - if (ch == EOF || ch == (int)'\n') break; + if (ch == EOF || ch == '\n') break; } } fclose(fp); @@ -2163,8 +2163,6 @@ void os::print_os_info(outputStream* st) { os::Posix::print_rlimit_info(st); - os::print_open_file_descriptors(st); - os::Posix::print_load_average(st); st->cr(); @@ -3437,7 +3435,7 @@ void os::Linux::rebuild_cpu_to_node_map() { } } } - FREE_C_HEAP_ARRAY(unsigned long, cpu_map); + FREE_C_HEAP_ARRAY(cpu_map); } int os::Linux::numa_node_to_cpus(int node, unsigned long *buffer, int bufferlen) { @@ -4384,7 +4382,7 @@ int os::Linux::get_namespace_pid(int vmid) { os::snprintf_checked(fname, sizeof(fname), "/proc/%d/status", vmid); FILE *fp = os::fopen(fname, "r"); - if (fp) { + if (fp != nullptr) { int pid, nspid; int ret; while (!feof(fp) && !ferror(fp)) { @@ -4398,7 +4396,7 @@ int os::Linux::get_namespace_pid(int vmid) { } for (;;) { int ch = fgetc(fp); - if (ch == EOF || ch == (int)'\n') break; + if (ch == EOF || ch == '\n') break; } } fclose(fp); diff --git a/src/hotspot/os/linux/os_perf_linux.cpp b/src/hotspot/os/linux/os_perf_linux.cpp index 9f91f3b4c0d..c0e863ed2a2 100644 --- a/src/hotspot/os/linux/os_perf_linux.cpp +++ b/src/hotspot/os/linux/os_perf_linux.cpp @@ -545,7 +545,7 @@ bool CPUPerformanceInterface::CPUPerformance::initialize() { CPUPerformanceInterface::CPUPerformance::~CPUPerformance() { if (_counters.cpus != nullptr) { - FREE_C_HEAP_ARRAY(char, _counters.cpus); + FREE_C_HEAP_ARRAY(_counters.cpus); } } @@ -811,7 +811,7 @@ int SystemProcessInterface::SystemProcesses::ProcessIterator::current(SystemProc cmdline = get_cmdline(); if (cmdline != nullptr) { process_info->set_command_line(allocate_string(cmdline)); - FREE_C_HEAP_ARRAY(char, cmdline); + FREE_C_HEAP_ARRAY(cmdline); } return OS_OK; @@ -937,12 +937,12 @@ CPUInformationInterface::~CPUInformationInterface() { if (_cpu_info != nullptr) { if (_cpu_info->cpu_name() != nullptr) { const char* cpu_name = _cpu_info->cpu_name(); - FREE_C_HEAP_ARRAY(char, cpu_name); + FREE_C_HEAP_ARRAY(cpu_name); _cpu_info->set_cpu_name(nullptr); } if (_cpu_info->cpu_description() != nullptr) { const char* cpu_desc = _cpu_info->cpu_description(); - FREE_C_HEAP_ARRAY(char, cpu_desc); + FREE_C_HEAP_ARRAY(cpu_desc); _cpu_info->set_cpu_description(nullptr); } delete _cpu_info; diff --git a/src/hotspot/os/linux/procMapsParser.cpp b/src/hotspot/os/linux/procMapsParser.cpp index 0663cae61f3..00675683e34 100644 --- a/src/hotspot/os/linux/procMapsParser.cpp +++ b/src/hotspot/os/linux/procMapsParser.cpp @@ -45,7 +45,7 @@ ProcSmapsParser::ProcSmapsParser(FILE* f) : } ProcSmapsParser::~ProcSmapsParser() { - FREE_C_HEAP_ARRAY(char, _line); + FREE_C_HEAP_ARRAY(_line); } bool ProcSmapsParser::read_line() { diff --git a/src/hotspot/os/posix/include/jvm_md.h b/src/hotspot/os/posix/include/jvm_md.h index eb8e1f0d7e9..061ef17aaae 100644 --- a/src/hotspot/os/posix/include/jvm_md.h +++ b/src/hotspot/os/posix/include/jvm_md.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -62,6 +62,19 @@ #define JVM_X_OK X_OK #define JVM_F_OK F_OK +#if defined(AIX) +#include "jni_md.h" +#include "dl_info.h" + +#ifdef __cplusplus +extern "C" { +#endif +JNIEXPORT int JVM_dladdr(void* addr, Dl_info* info); +#ifdef __cplusplus +} +#endif +#endif + /* * File I/O */ diff --git a/src/hotspot/os/posix/perfMemory_posix.cpp b/src/hotspot/os/posix/perfMemory_posix.cpp index b8be77c5e05..300c86ffc47 100644 --- a/src/hotspot/os/posix/perfMemory_posix.cpp +++ b/src/hotspot/os/posix/perfMemory_posix.cpp @@ -118,7 +118,7 @@ static void save_memory_to_file(char* addr, size_t size) { } } } - FREE_C_HEAP_ARRAY(char, destfile); + FREE_C_HEAP_ARRAY(destfile); } @@ -483,14 +483,14 @@ static char* get_user_name(uid_t uid) { p->pw_name == nullptr ? "pw_name = null" : "pw_name zero length"); } } - FREE_C_HEAP_ARRAY(char, pwbuf); + FREE_C_HEAP_ARRAY(pwbuf); return nullptr; } char* user_name = NEW_C_HEAP_ARRAY(char, strlen(p->pw_name) + 1, mtInternal); strcpy(user_name, p->pw_name); - FREE_C_HEAP_ARRAY(char, pwbuf); + FREE_C_HEAP_ARRAY(pwbuf); return user_name; } @@ -572,7 +572,7 @@ static char* get_user_name_slow(int vmid, int nspid, TRAPS) { DIR* subdirp = open_directory_secure(usrdir_name); if (subdirp == nullptr) { - FREE_C_HEAP_ARRAY(char, usrdir_name); + FREE_C_HEAP_ARRAY(usrdir_name); continue; } @@ -583,7 +583,7 @@ static char* get_user_name_slow(int vmid, int nspid, TRAPS) { // symlink can be exploited. // if (!is_directory_secure(usrdir_name)) { - FREE_C_HEAP_ARRAY(char, usrdir_name); + FREE_C_HEAP_ARRAY(usrdir_name); os::closedir(subdirp); continue; } @@ -607,13 +607,13 @@ static char* get_user_name_slow(int vmid, int nspid, TRAPS) { // don't follow symbolic links for the file RESTARTABLE(::lstat(filename, &statbuf), result); if (result == OS_ERR) { - FREE_C_HEAP_ARRAY(char, filename); + FREE_C_HEAP_ARRAY(filename); continue; } // skip over files that are not regular files. if (!S_ISREG(statbuf.st_mode)) { - FREE_C_HEAP_ARRAY(char, filename); + FREE_C_HEAP_ARRAY(filename); continue; } @@ -623,7 +623,7 @@ static char* get_user_name_slow(int vmid, int nspid, TRAPS) { if (statbuf.st_ctime > oldest_ctime) { char* user = strchr(dentry->d_name, '_') + 1; - FREE_C_HEAP_ARRAY(char, oldest_user); + FREE_C_HEAP_ARRAY(oldest_user); oldest_user = NEW_C_HEAP_ARRAY(char, strlen(user)+1, mtInternal); strcpy(oldest_user, user); @@ -631,11 +631,11 @@ static char* get_user_name_slow(int vmid, int nspid, TRAPS) { } } - FREE_C_HEAP_ARRAY(char, filename); + FREE_C_HEAP_ARRAY(filename); } } os::closedir(subdirp); - FREE_C_HEAP_ARRAY(char, usrdir_name); + FREE_C_HEAP_ARRAY(usrdir_name); } os::closedir(tmpdirp); @@ -701,6 +701,39 @@ static void remove_file(const char* path) { } } +// Files newer than this threshold are considered to belong to a JVM that may +// still be starting up and are therefore not candidates for stale-file +// cleanup. This avoids racing a concurrent JVM startup while scanning the +// hsperfdata directory. +static const time_t cleanup_grace_period_seconds = 5; + +static bool is_cleanup_candidate(const char* filename, const char* dirname) { + struct stat statbuf; + int result; + + RESTARTABLE(::lstat(filename, &statbuf), result); + if (result == OS_ERR) { + log_debug(perf, memops)("lstat failed for %s/%s: %s", dirname, filename, os::strerror(errno)); + return false; + } + + if (!S_ISREG(statbuf.st_mode)) { + return false; + } + + const time_t now = time(nullptr); + if (now == (time_t)-1) { + return false; + } + + if (statbuf.st_mtime >= now - cleanup_grace_period_seconds) { + log_debug(perf, memops)("Skip cleanup of fresh file %s/%s", dirname, filename); + return false; + } + + return true; +} + // cleanup stale shared memory files // // This method attempts to remove all stale shared memory files in @@ -744,6 +777,11 @@ static void cleanup_sharedmem_files(const char* dirname) { continue; } + if (!is_cleanup_candidate(filename, dirname)) { + errno = 0; + continue; + } + #if defined(LINUX) // Special case on Linux, if multiple containers share the // same /tmp directory: @@ -872,16 +910,56 @@ static int create_sharedmem_file(const char* dirname, const char* filename, size return -1; } - // Open the filename in the current directory. - // Cannot use O_TRUNC here; truncation of an existing file has to happen - // after the is_file_secure() check below. - int fd; - RESTARTABLE(os::open(filename, O_RDWR|O_CREAT|O_NOFOLLOW, S_IRUSR|S_IWUSR), fd); + int fd = OS_ERR; + static const int create_sharedmem_file_retry_count = LINUX_ONLY(3) NOT_LINUX(1); + for (int attempt = 0; attempt < create_sharedmem_file_retry_count; attempt++) { + // Open the filename in the current directory. + // Use O_EXCL so that startup never reuses an existing pid file unless it + // has first been proven stale and removed in `cleanup_sharedmem_files`. + RESTARTABLE(os::open(filename, O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW, S_IRUSR|S_IWUSR), fd); + if (fd == OS_ERR) { + break; + } + +#if defined(LINUX) + // On Linux, different containerized processes that share the same /tmp + // directory (e.g., with "docker --volume ...") may have the same pid and + // try to use the same file. To avoid conflicts among such processes, we + // allow only one of them (the winner of the flock() call) to write to the + // file. If we lose the race, assume we may have collided with a concurrent + // scavenger briefly holding the lock on a fresh file and retry a few times + // before giving up. + int n; + RESTARTABLE(::flock(fd, LOCK_EX|LOCK_NB), n); + if (n == 0) { + break; + } + + const int flock_errno = errno; + ::close(fd); + fd = OS_ERR; + + if (attempt + 1 == create_sharedmem_file_retry_count || flock_errno != EWOULDBLOCK) { + log_warning(perf, memops)("Cannot use file %s/%s because %s (errno = %d)", dirname, filename, + (flock_errno == EWOULDBLOCK) ? + "it is locked by another process" : + "flock() failed", flock_errno); + errno = flock_errno; + break; + } + + // Short sleep to allow the lock to free up. + os::naked_short_sleep(1); +#endif + } + if (fd == OS_ERR) { if (log_is_enabled(Debug, perf)) { LogStreamHandle(Debug, perf) log; if (errno == ELOOP) { log.print_cr("file %s is a symlink and is not secure", filename); + } else if (errno == EEXIST) { + log.print_cr("could not create file %s: existing file is not provably stale", filename); } else { log.print_cr("could not create file %s: %s", filename, os::strerror(errno)); } @@ -901,27 +979,7 @@ static int create_sharedmem_file(const char* dirname, const char* filename, size } #if defined(LINUX) - // On Linux, different containerized processes that share the same /tmp - // directory (e.g., with "docker --volume ...") may have the same pid and - // try to use the same file. To avoid conflicts among such - // processes, we allow only one of them (the winner of the flock() call) - // to write to the file. All the other processes will give up and will - // have perfdata disabled. - // - // Note that the flock will be automatically given up when the winner - // process exits. - // - // The locking protocol works only with other JVMs that have the JDK-8286030 - // fix. If you are sharing the /tmp difrectory among different containers, - // do not use older JVMs that don't have this fix, or the behavior is undefined. - int n; - RESTARTABLE(::flock(fd, LOCK_EX|LOCK_NB), n); - if (n != 0) { - log_warning(perf, memops)("Cannot use file %s/%s because %s (errno = %d)", dirname, filename, - (errno == EWOULDBLOCK) ? - "it is locked by another process" : - "flock() failed", errno); - ::close(fd); + if (fd == OS_ERR) { return -1; } #endif @@ -1047,11 +1105,11 @@ static char* mmap_create_shared(size_t size) { log_info(perf, memops)("Trying to open %s/%s", dirname, short_filename); fd = create_sharedmem_file(dirname, short_filename, size); - FREE_C_HEAP_ARRAY(char, user_name); - FREE_C_HEAP_ARRAY(char, dirname); + FREE_C_HEAP_ARRAY(user_name); + FREE_C_HEAP_ARRAY(dirname); if (fd == -1) { - FREE_C_HEAP_ARRAY(char, filename); + FREE_C_HEAP_ARRAY(filename); return nullptr; } @@ -1063,7 +1121,7 @@ static char* mmap_create_shared(size_t size) { if (mapAddress == MAP_FAILED) { log_debug(perf)("mmap failed - %s", os::strerror(errno)); remove_file(filename); - FREE_C_HEAP_ARRAY(char, filename); + FREE_C_HEAP_ARRAY(filename); return nullptr; } @@ -1113,7 +1171,7 @@ static void delete_shared_memory(char* addr, size_t size) { remove_file(backing_store_file_name); // Don't.. Free heap memory could deadlock os::abort() if it is called // from signal handler. OS will reclaim the heap memory. - // FREE_C_HEAP_ARRAY(char, backing_store_file_name); + // FREE_C_HEAP_ARRAY(backing_store_file_name); backing_store_file_name = nullptr; } } @@ -1165,8 +1223,8 @@ static void mmap_attach_shared(int vmid, char** addr, size_t* sizep, TRAPS) { // store file, we don't follow them when attaching either. // if (!is_directory_secure(dirname)) { - FREE_C_HEAP_ARRAY(char, dirname); - FREE_C_HEAP_ARRAY(char, luser); + FREE_C_HEAP_ARRAY(dirname); + FREE_C_HEAP_ARRAY(luser); THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "Process not found"); } @@ -1178,9 +1236,9 @@ static void mmap_attach_shared(int vmid, char** addr, size_t* sizep, TRAPS) { int fd = open_sharedmem_file(filename, file_flags, THREAD); // free the c heap resources that are no longer needed - FREE_C_HEAP_ARRAY(char, luser); - FREE_C_HEAP_ARRAY(char, dirname); - FREE_C_HEAP_ARRAY(char, filename); + FREE_C_HEAP_ARRAY(luser); + FREE_C_HEAP_ARRAY(dirname); + FREE_C_HEAP_ARRAY(filename); if (HAS_PENDING_EXCEPTION) { assert(fd == OS_ERR, "open_sharedmem_file always return OS_ERR on exceptions"); diff --git a/src/hotspot/os/windows/os_perf_windows.cpp b/src/hotspot/os/windows/os_perf_windows.cpp index 9d04ae65954..59ea83b9148 100644 --- a/src/hotspot/os/windows/os_perf_windows.cpp +++ b/src/hotspot/os/windows/os_perf_windows.cpp @@ -178,9 +178,9 @@ static void destroy(MultiCounterQueryP query) { for (int i = 0; i < query->noOfCounters; ++i) { close_query(nullptr, &query->counters[i]); } - FREE_C_HEAP_ARRAY(char, query->counters); + FREE_C_HEAP_ARRAY(query->counters); close_query(&query->query.pdh_query_handle, nullptr); - FREE_C_HEAP_ARRAY(MultiCounterQueryS, query); + FREE_C_HEAP_ARRAY(query); } } @@ -189,15 +189,15 @@ static void destroy_query_set(MultiCounterQuerySetP query_set) { for (int j = 0; j < query_set->queries[i].noOfCounters; ++j) { close_query(nullptr, &query_set->queries[i].counters[j]); } - FREE_C_HEAP_ARRAY(char, query_set->queries[i].counters); + FREE_C_HEAP_ARRAY(query_set->queries[i].counters); close_query(&query_set->queries[i].query.pdh_query_handle, nullptr); } - FREE_C_HEAP_ARRAY(MultiCounterQueryS, query_set->queries); + FREE_C_HEAP_ARRAY(query_set->queries); } static void destroy(MultiCounterQuerySetP query) { destroy_query_set(query); - FREE_C_HEAP_ARRAY(MultiCounterQuerySetS, query); + FREE_C_HEAP_ARRAY(query); } static void destroy(ProcessQueryP query) { @@ -229,7 +229,7 @@ static void allocate_counters(ProcessQueryP query, size_t nofCounters) { } static void deallocate_counters(MultiCounterQueryP query) { - FREE_C_HEAP_ARRAY(char, query->counters); + FREE_C_HEAP_ARRAY(query->counters); query->counters = nullptr; query->noOfCounters = 0; } @@ -710,11 +710,11 @@ static const char* pdh_process_image_name() { } static void deallocate_pdh_constants() { - FREE_C_HEAP_ARRAY(char, process_image_name); + FREE_C_HEAP_ARRAY(process_image_name); process_image_name = nullptr; - FREE_C_HEAP_ARRAY(char, pdh_process_instance_IDProcess_counter_fmt); + FREE_C_HEAP_ARRAY(pdh_process_instance_IDProcess_counter_fmt); pdh_process_instance_IDProcess_counter_fmt = nullptr; - FREE_C_HEAP_ARRAY(char, pdh_process_instance_wildcard_IDProcess_counter); + FREE_C_HEAP_ARRAY(pdh_process_instance_wildcard_IDProcess_counter); pdh_process_instance_wildcard_IDProcess_counter = nullptr; } @@ -1445,9 +1445,9 @@ bool CPUInformationInterface::initialize() { CPUInformationInterface::~CPUInformationInterface() { if (_cpu_info != nullptr) { - FREE_C_HEAP_ARRAY(char, _cpu_info->cpu_name()); + FREE_C_HEAP_ARRAY(_cpu_info->cpu_name()); _cpu_info->set_cpu_name(nullptr); - FREE_C_HEAP_ARRAY(char, _cpu_info->cpu_description()); + FREE_C_HEAP_ARRAY(_cpu_info->cpu_description()); _cpu_info->set_cpu_description(nullptr); delete _cpu_info; } diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index 18d047348cb..9a987bf3762 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -334,14 +334,14 @@ void os::init_system_properties_values() { home_path = NEW_C_HEAP_ARRAY(char, strlen(home_dir) + 1, mtInternal); strcpy(home_path, home_dir); Arguments::set_java_home(home_path); - FREE_C_HEAP_ARRAY(char, home_path); + FREE_C_HEAP_ARRAY(home_path); dll_path = NEW_C_HEAP_ARRAY(char, strlen(home_dir) + strlen(bin) + 1, mtInternal); strcpy(dll_path, home_dir); strcat(dll_path, bin); Arguments::set_dll_dir(dll_path); - FREE_C_HEAP_ARRAY(char, dll_path); + FREE_C_HEAP_ARRAY(dll_path); if (!set_boot_path('\\', ';')) { vm_exit_during_initialization("Failed setting boot class path.", nullptr); @@ -396,7 +396,7 @@ void os::init_system_properties_values() { strcat(library_path, ";."); Arguments::set_library_path(library_path); - FREE_C_HEAP_ARRAY(char, library_path); + FREE_C_HEAP_ARRAY(library_path); } // Default extensions directory @@ -1079,7 +1079,7 @@ void os::set_native_thread_name(const char *name) { HRESULT hr = _SetThreadDescription(current, unicode_name); if (FAILED(hr)) { log_debug(os, thread)("set_native_thread_name: SetThreadDescription failed - falling back to debugger method"); - FREE_C_HEAP_ARRAY(WCHAR, unicode_name); + FREE_C_HEAP_ARRAY(unicode_name); } else { log_trace(os, thread)("set_native_thread_name: SetThreadDescription succeeded - new name: %s", name); @@ -1102,7 +1102,7 @@ void os::set_native_thread_name(const char *name) { LocalFree(thread_name); } #endif - FREE_C_HEAP_ARRAY(WCHAR, unicode_name); + FREE_C_HEAP_ARRAY(unicode_name); return; } } else { @@ -2528,12 +2528,6 @@ LONG Handle_Exception(struct _EXCEPTION_POINTERS* exceptionInfo, return EXCEPTION_CONTINUE_EXECUTION; } - -// Used for PostMortemDump -extern "C" void safepoints(); -extern "C" void find(int x); -extern "C" void events(); - // According to Windows API documentation, an illegal instruction sequence should generate // the 0xC000001C exception code. However, real world experience shows that occasionnaly // the execution of an illegal instruction can generate the exception code 0xC000001E. This @@ -2903,7 +2897,7 @@ class NUMANodeListHolder { int _numa_used_node_count; void free_node_list() { - FREE_C_HEAP_ARRAY(int, _numa_used_node_list); + FREE_C_HEAP_ARRAY(_numa_used_node_list); } public: @@ -4750,7 +4744,7 @@ static wchar_t* wide_abs_unc_path(char const* path, errno_t & err, int additiona LPWSTR unicode_path = nullptr; err = convert_to_unicode(buf, &unicode_path); - FREE_C_HEAP_ARRAY(char, buf); + FREE_C_HEAP_ARRAY(buf); if (err != ERROR_SUCCESS) { return nullptr; } @@ -4778,9 +4772,9 @@ static wchar_t* wide_abs_unc_path(char const* path, errno_t & err, int additiona } if (converted_path != unicode_path) { - FREE_C_HEAP_ARRAY(WCHAR, converted_path); + FREE_C_HEAP_ARRAY(converted_path); } - FREE_C_HEAP_ARRAY(WCHAR, unicode_path); + FREE_C_HEAP_ARRAY(unicode_path); return static_cast(result); // LPWSTR and wchat_t* are the same type on Windows. } @@ -5833,7 +5827,7 @@ int os::fork_and_exec(const char* cmd) { exit_code = -1; } - FREE_C_HEAP_ARRAY(char, cmd_string); + FREE_C_HEAP_ARRAY(cmd_string); return (int)exit_code; } diff --git a/src/hotspot/os/windows/perfMemory_windows.cpp b/src/hotspot/os/windows/perfMemory_windows.cpp index dad2804f18a..8e698c53d28 100644 --- a/src/hotspot/os/windows/perfMemory_windows.cpp +++ b/src/hotspot/os/windows/perfMemory_windows.cpp @@ -113,7 +113,7 @@ static void save_memory_to_file(char* addr, size_t size) { } } - FREE_C_HEAP_ARRAY(char, destfile); + FREE_C_HEAP_ARRAY(destfile); } // Shared Memory Implementation Details @@ -319,7 +319,7 @@ static char* get_user_name_slow(int vmid) { DIR* subdirp = os::opendir(usrdir_name); if (subdirp == nullptr) { - FREE_C_HEAP_ARRAY(char, usrdir_name); + FREE_C_HEAP_ARRAY(usrdir_name); continue; } @@ -330,7 +330,7 @@ static char* get_user_name_slow(int vmid) { // symlink can be exploited. // if (!is_directory_secure(usrdir_name)) { - FREE_C_HEAP_ARRAY(char, usrdir_name); + FREE_C_HEAP_ARRAY(usrdir_name); os::closedir(subdirp); continue; } @@ -350,13 +350,13 @@ static char* get_user_name_slow(int vmid) { strcat(filename, udentry->d_name); if (::stat(filename, &statbuf) == OS_ERR) { - FREE_C_HEAP_ARRAY(char, filename); + FREE_C_HEAP_ARRAY(filename); continue; } // skip over files that are not regular files. if ((statbuf.st_mode & S_IFMT) != S_IFREG) { - FREE_C_HEAP_ARRAY(char, filename); + FREE_C_HEAP_ARRAY(filename); continue; } @@ -378,18 +378,18 @@ static char* get_user_name_slow(int vmid) { if (statbuf.st_ctime > latest_ctime) { char* user = strchr(dentry->d_name, '_') + 1; - FREE_C_HEAP_ARRAY(char, latest_user); + FREE_C_HEAP_ARRAY(latest_user); latest_user = NEW_C_HEAP_ARRAY(char, strlen(user)+1, mtInternal); strcpy(latest_user, user); latest_ctime = statbuf.st_ctime; } - FREE_C_HEAP_ARRAY(char, filename); + FREE_C_HEAP_ARRAY(filename); } } os::closedir(subdirp); - FREE_C_HEAP_ARRAY(char, usrdir_name); + FREE_C_HEAP_ARRAY(usrdir_name); } os::closedir(tmpdirp); @@ -481,7 +481,7 @@ static void remove_file(const char* dirname, const char* filename) { } } - FREE_C_HEAP_ARRAY(char, path); + FREE_C_HEAP_ARRAY(path); } // returns true if the process represented by pid is alive, otherwise @@ -708,11 +708,11 @@ static void free_security_desc(PSECURITY_DESCRIPTOR pSD) { // be an ACL we enlisted. free the resources. // if (success && exists && pACL != nullptr && !isdefault) { - FREE_C_HEAP_ARRAY(char, pACL); + FREE_C_HEAP_ARRAY(pACL); } // free the security descriptor - FREE_C_HEAP_ARRAY(char, pSD); + FREE_C_HEAP_ARRAY(pSD); } } @@ -768,7 +768,7 @@ static PSID get_user_sid(HANDLE hProcess) { if (!GetTokenInformation(hAccessToken, TokenUser, token_buf, rsize, &rsize)) { log_debug(perf)("GetTokenInformation failure: lasterror = %d, rsize = %d", GetLastError(), rsize); - FREE_C_HEAP_ARRAY(char, token_buf); + FREE_C_HEAP_ARRAY(token_buf); CloseHandle(hAccessToken); return nullptr; } @@ -779,15 +779,15 @@ static PSID get_user_sid(HANDLE hProcess) { if (!CopySid(nbytes, pSID, token_buf->User.Sid)) { log_debug(perf)("GetTokenInformation failure: lasterror = %d, rsize = %d", GetLastError(), rsize); - FREE_C_HEAP_ARRAY(char, token_buf); - FREE_C_HEAP_ARRAY(char, pSID); + FREE_C_HEAP_ARRAY(token_buf); + FREE_C_HEAP_ARRAY(pSID); CloseHandle(hAccessToken); return nullptr; } // close the access token. CloseHandle(hAccessToken); - FREE_C_HEAP_ARRAY(char, token_buf); + FREE_C_HEAP_ARRAY(token_buf); return pSID; } @@ -865,7 +865,7 @@ static bool add_allow_aces(PSECURITY_DESCRIPTOR pSD, if (!InitializeAcl(newACL, newACLsize, ACL_REVISION)) { log_debug(perf)("InitializeAcl failure: lasterror = %d", GetLastError()); - FREE_C_HEAP_ARRAY(char, newACL); + FREE_C_HEAP_ARRAY(newACL); return false; } @@ -876,7 +876,7 @@ static bool add_allow_aces(PSECURITY_DESCRIPTOR pSD, LPVOID ace; if (!GetAce(oldACL, ace_index, &ace)) { log_debug(perf)("InitializeAcl failure: lasterror = %d", GetLastError()); - FREE_C_HEAP_ARRAY(char, newACL); + FREE_C_HEAP_ARRAY(newACL); return false; } if (((ACCESS_ALLOWED_ACE *)ace)->Header.AceFlags && INHERITED_ACE) { @@ -901,7 +901,7 @@ static bool add_allow_aces(PSECURITY_DESCRIPTOR pSD, if (!AddAce(newACL, ACL_REVISION, MAXDWORD, ace, ((PACE_HEADER)ace)->AceSize)) { log_debug(perf)("AddAce failure: lasterror = %d", GetLastError()); - FREE_C_HEAP_ARRAY(char, newACL); + FREE_C_HEAP_ARRAY(newACL); return false; } } @@ -915,7 +915,7 @@ static bool add_allow_aces(PSECURITY_DESCRIPTOR pSD, aces[i].mask, aces[i].pSid)) { log_debug(perf)("AddAccessAllowedAce failure: lasterror = %d", GetLastError()); - FREE_C_HEAP_ARRAY(char, newACL); + FREE_C_HEAP_ARRAY(newACL); return false; } } @@ -928,13 +928,13 @@ static bool add_allow_aces(PSECURITY_DESCRIPTOR pSD, LPVOID ace; if (!GetAce(oldACL, ace_index, &ace)) { log_debug(perf)("InitializeAcl failure: lasterror = %d", GetLastError()); - FREE_C_HEAP_ARRAY(char, newACL); + FREE_C_HEAP_ARRAY(newACL); return false; } if (!AddAce(newACL, ACL_REVISION, MAXDWORD, ace, ((PACE_HEADER)ace)->AceSize)) { log_debug(perf)("AddAce failure: lasterror = %d", GetLastError()); - FREE_C_HEAP_ARRAY(char, newACL); + FREE_C_HEAP_ARRAY(newACL); return false; } ace_index++; @@ -944,7 +944,7 @@ static bool add_allow_aces(PSECURITY_DESCRIPTOR pSD, // add the new ACL to the security descriptor. if (!SetSecurityDescriptorDacl(pSD, TRUE, newACL, FALSE)) { log_debug(perf)("SetSecurityDescriptorDacl failure: lasterror = %d", GetLastError()); - FREE_C_HEAP_ARRAY(char, newACL); + FREE_C_HEAP_ARRAY(newACL); return false; } @@ -952,7 +952,7 @@ static bool add_allow_aces(PSECURITY_DESCRIPTOR pSD, // protected prevents that. if (!SetSecurityDescriptorControl(pSD, SE_DACL_PROTECTED, SE_DACL_PROTECTED)) { log_debug(perf)("SetSecurityDescriptorControl failure: lasterror = %d", GetLastError()); - FREE_C_HEAP_ARRAY(char, newACL); + FREE_C_HEAP_ARRAY(newACL); return false; } @@ -1057,7 +1057,7 @@ static LPSECURITY_ATTRIBUTES make_user_everybody_admin_security_attr( // create a security attributes structure with access control // entries as initialized above. LPSECURITY_ATTRIBUTES lpSA = make_security_attr(aces, 3); - FREE_C_HEAP_ARRAY(char, aces[0].pSid); + FREE_C_HEAP_ARRAY(aces[0].pSid); FreeSid(everybodySid); FreeSid(administratorsSid); return(lpSA); @@ -1341,8 +1341,8 @@ static char* mapping_create_shared(size_t size) { // check that the file system is secure - i.e. it supports ACLs. if (!is_filesystem_secure(dirname)) { - FREE_C_HEAP_ARRAY(char, dirname); - FREE_C_HEAP_ARRAY(char, user); + FREE_C_HEAP_ARRAY(dirname); + FREE_C_HEAP_ARRAY(user); return nullptr; } @@ -1358,15 +1358,15 @@ static char* mapping_create_shared(size_t size) { assert(((size != 0) && (size % os::vm_page_size() == 0)), "unexpected PerfMemry region size"); - FREE_C_HEAP_ARRAY(char, user); + FREE_C_HEAP_ARRAY(user); // create the shared memory resources sharedmem_fileMapHandle = create_sharedmem_resources(dirname, filename, objectname, size); - FREE_C_HEAP_ARRAY(char, filename); - FREE_C_HEAP_ARRAY(char, objectname); - FREE_C_HEAP_ARRAY(char, dirname); + FREE_C_HEAP_ARRAY(filename); + FREE_C_HEAP_ARRAY(objectname); + FREE_C_HEAP_ARRAY(dirname); if (sharedmem_fileMapHandle == nullptr) { return nullptr; @@ -1480,8 +1480,8 @@ static void open_file_mapping(int vmid, char** addrp, size_t* sizep, TRAPS) { // store file, we also don't following them when attaching // if (!is_directory_secure(dirname)) { - FREE_C_HEAP_ARRAY(char, dirname); - FREE_C_HEAP_ARRAY(char, luser); + FREE_C_HEAP_ARRAY(dirname); + FREE_C_HEAP_ARRAY(luser); THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "Process not found"); } @@ -1498,10 +1498,10 @@ static void open_file_mapping(int vmid, char** addrp, size_t* sizep, TRAPS) { char* robjectname = ResourceArea::strdup(THREAD, objectname); // free the c heap resources that are no longer needed - FREE_C_HEAP_ARRAY(char, luser); - FREE_C_HEAP_ARRAY(char, dirname); - FREE_C_HEAP_ARRAY(char, filename); - FREE_C_HEAP_ARRAY(char, objectname); + FREE_C_HEAP_ARRAY(luser); + FREE_C_HEAP_ARRAY(dirname); + FREE_C_HEAP_ARRAY(filename); + FREE_C_HEAP_ARRAY(objectname); size_t size; if (*sizep == 0) { diff --git a/src/hotspot/os_cpu/bsd_aarch64/os_bsd_aarch64.cpp b/src/hotspot/os_cpu/bsd_aarch64/os_bsd_aarch64.cpp index 36599594842..49d879731ff 100644 --- a/src/hotspot/os_cpu/bsd_aarch64/os_bsd_aarch64.cpp +++ b/src/hotspot/os_cpu/bsd_aarch64/os_bsd_aarch64.cpp @@ -620,6 +620,8 @@ extern "C" { assert(VM_Version::supports_sb(), "current CPU does not support SB instruction"); asm volatile(".inst 0xd50330ff" : : : "memory"); break; + case SpinWait::WFET: + ShouldNotReachHere(); #ifdef ASSERT default: ShouldNotReachHere(); diff --git a/src/hotspot/os_cpu/linux_aarch64/ic_ivau_probe_linux_aarch64.S b/src/hotspot/os_cpu/linux_aarch64/ic_ivau_probe_linux_aarch64.S new file mode 100644 index 00000000000..b82053d37b9 --- /dev/null +++ b/src/hotspot/os_cpu/linux_aarch64/ic_ivau_probe_linux_aarch64.S @@ -0,0 +1,69 @@ +/* + * Copyright Amazon.com Inc. 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 "defs.S.inc" + + # Probe whether IC IVAU is trapped. + # + # Returns 1 if IC IVAU is trapped (did not fault), 0 if not trapped + # (faulted on VA 0x0, signal handler redirected to continuation). + # + # int ic_ivau_probe(void); +DECLARE_FUNC(ic_ivau_probe): +DECLARE_FUNC(_ic_ivau_probe_fault): + ic ivau, xzr + mov x0, #1 + ret +DECLARE_FUNC(_ic_ivau_probe_continuation): + mov x0, #0 + ret + +/* Emit .note.gnu.property section in case of PAC or BTI being enabled. */ +#ifdef __ARM_FEATURE_BTI_DEFAULT + #ifdef __ARM_FEATURE_PAC_DEFAULT + #define GNU_PROPERTY_AARCH64_FEATURE 3 + #else + #define GNU_PROPERTY_AARCH64_FEATURE 1 + #endif +#else + #ifdef __ARM_FEATURE_PAC_DEFAULT + #define GNU_PROPERTY_AARCH64_FEATURE 2 + #else + #define GNU_PROPERTY_AARCH64_FEATURE 0 + #endif +#endif + +#if (GNU_PROPERTY_AARCH64_FEATURE != 0) + .pushsection .note.gnu.property, "a" + .align 3 + .long 4 /* name length */ + .long 0x10 /* data length */ + .long 5 /* note type: NT_GNU_PROPERTY_TYPE_0 */ + .string "GNU" /* vendor name */ + .long 0xc0000000 /* GNU_PROPERTY_AARCH64_FEATURE_1_AND */ + .long 4 /* pr_datasze */ + .long GNU_PROPERTY_AARCH64_FEATURE + .long 0 + .popsection +#endif diff --git a/src/hotspot/os_cpu/linux_aarch64/icache_linux_aarch64.cpp b/src/hotspot/os_cpu/linux_aarch64/icache_linux_aarch64.cpp new file mode 100644 index 00000000000..11911a48e06 --- /dev/null +++ b/src/hotspot/os_cpu/linux_aarch64/icache_linux_aarch64.cpp @@ -0,0 +1,28 @@ +/* + * Copyright Amazon.com Inc. 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 "runtime/icache.hpp" +#include "utilities/globalDefinitions.hpp" + +DEBUG_ONLY(THREAD_LOCAL AArch64ICacheInvalidationContext* AArch64ICacheInvalidationContext::_current_context = nullptr;) diff --git a/src/hotspot/os_cpu/linux_aarch64/icache_linux_aarch64.hpp b/src/hotspot/os_cpu/linux_aarch64/icache_linux_aarch64.hpp index 8fbaa7a6b6e..5121a875701 100644 --- a/src/hotspot/os_cpu/linux_aarch64/icache_linux_aarch64.hpp +++ b/src/hotspot/os_cpu/linux_aarch64/icache_linux_aarch64.hpp @@ -26,6 +26,11 @@ #ifndef OS_CPU_LINUX_AARCH64_ICACHE_AARCH64_HPP #define OS_CPU_LINUX_AARCH64_ICACHE_AARCH64_HPP +#include "memory/allocation.hpp" +#include "runtime/vm_version.hpp" +#include "utilities/globalDefinitions.hpp" +#include "vm_version_aarch64.hpp" + // Interface for updating the instruction cache. Whenever the VM // modifies code, part of the processor instruction cache potentially // has to be flushed. @@ -37,8 +42,105 @@ class ICache : public AbstractICache { __builtin___clear_cache((char *)addr, (char *)(addr + 4)); } static void invalidate_range(address start, int nbytes) { - __builtin___clear_cache((char *)start, (char *)(start + nbytes)); + if (NeoverseN1ICacheErratumMitigation) { + assert(VM_Version::is_cache_idc_enabled(), + "Expect CTR_EL0.IDC to be enabled for Neoverse N1 with erratum " + "1542419"); + assert(!VM_Version::is_cache_dic_enabled(), + "Expect CTR_EL0.DIC to be disabled for Neoverse N1 with erratum " + "1542419"); + assert(VM_Version::is_ic_ivau_trapped(), "Expect 'ic ivau, xzr' to be trapped"); + asm volatile("dsb ish \n" + "ic ivau, xzr \n" + "dsb ish \n" + "isb \n" + : : : "memory"); + } else { + __builtin___clear_cache((char *)start, (char *)(start + nbytes)); + } } }; +class AArch64ICacheInvalidationContext : StackObj { + private: + +#ifdef ASSERT + static THREAD_LOCAL AArch64ICacheInvalidationContext* _current_context; +#endif + + bool _has_modified_code; + + public: + NONCOPYABLE(AArch64ICacheInvalidationContext); + + AArch64ICacheInvalidationContext() + : _has_modified_code(false) { + assert(_current_context == nullptr, "nested ICacheInvalidationContext not supported"); +#ifdef ASSERT + _current_context = this; +#endif + } + + ~AArch64ICacheInvalidationContext() { + DEBUG_ONLY(_current_context = nullptr); + + if (!_has_modified_code || !UseSingleICacheInvalidation) { + return; + } + + assert(VM_Version::is_cache_idc_enabled(), "Expect CTR_EL0.IDC to be enabled"); + + asm volatile("dsb ish" : : : "memory"); + + if (NeoverseN1ICacheErratumMitigation) { + assert(!VM_Version::is_cache_dic_enabled(), + "Expect CTR_EL0.DIC to be disabled for Neoverse N1 with erratum " + "1542419"); + assert(VM_Version::is_ic_ivau_trapped(), "Expect 'ic ivau, xzr' to be trapped"); + + // Errata 1542419: Neoverse N1 cores with the 'COHERENT_ICACHE' feature + // may fetch stale instructions when software depends on + // prefetch-speculation-protection instead of explicit synchronization. + // + // Neoverse-N1 implementation mitigates the errata 1542419 with a + // workaround: + // - Disable coherent icache. + // - Trap IC IVAU instructions. + // - Execute: + // - tlbi vae3is, xzr + // - dsb sy + // - Ignore trapped IC IVAU instructions. + // + // `tlbi vae3is, xzr` invalidates all translation entries (all VAs, all + // possible levels). It waits for all memory accesses using in-scope old + // translation information to complete before it is considered complete. + // + // As this workaround has significant overhead, Arm Neoverse N1 (MP050) + // Software Developer Errata Notice version 29.0 suggests: + // + // "Since one TLB inner-shareable invalidation is enough to avoid this + // erratum, the number of injected TLB invalidations should be minimized + // in the trap handler to mitigate the performance impact due to this + // workaround." + // As the address for icache invalidation is not relevant and + // IC IVAU instruction is ignored, we use XZR in it. + asm volatile( + "ic ivau, xzr \n" + "dsb ish \n" + : + : + : "memory"); + } else { + assert(VM_Version::is_cache_dic_enabled(), "Expect CTR_EL0.DIC to be enabled"); + } + asm volatile("isb" : : : "memory"); + } + + void set_has_modified_code() { + _has_modified_code = true; + } +}; + +#define PD_ICACHE_INVALIDATION_CONTEXT AArch64ICacheInvalidationContext + #endif // OS_CPU_LINUX_AARCH64_ICACHE_AARCH64_HPP diff --git a/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp b/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp index da9e7e159f1..67e0569bf31 100644 --- a/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp +++ b/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp @@ -77,6 +77,11 @@ #define REG_LR 30 #define REG_BCP 22 +// IC IVAU trap probe. +// Defined in ic_ivau_probe_linux_aarch64.S. +extern "C" char _ic_ivau_probe_fault[] __attribute__ ((visibility ("hidden"))); +extern "C" char _ic_ivau_probe_continuation[] __attribute__ ((visibility ("hidden"))); + NOINLINE address os::current_stack_pointer() { return (address)__builtin_frame_address(0); } @@ -228,6 +233,12 @@ bool PosixSignals::pd_hotspot_signal_handler(int sig, siginfo_t* info, } } + // IC IVAU trap probe during VM_Version initialization. + // If IC IVAU is not trapped, it faults on unmapped VA 0x0. + if (sig == SIGSEGV && pc == (address)_ic_ivau_probe_fault) { + stub = (address)_ic_ivau_probe_continuation; + } + if (thread->thread_state() == _thread_in_Java) { // Java thread running in Java code => find exception handler if any // a fault inside compiled code, the interpreter, or a stub diff --git a/src/hotspot/os_cpu/linux_aarch64/vm_version_linux_aarch64.cpp b/src/hotspot/os_cpu/linux_aarch64/vm_version_linux_aarch64.cpp index 1fe06dc640d..ee2d3013c4c 100644 --- a/src/hotspot/os_cpu/linux_aarch64/vm_version_linux_aarch64.cpp +++ b/src/hotspot/os_cpu/linux_aarch64/vm_version_linux_aarch64.cpp @@ -31,6 +31,10 @@ #include #include +// IC IVAU trap probe. +// Defined in ic_ivau_probe_linux_aarch64.S. +extern "C" int ic_ivau_probe(void); + #ifndef HWCAP_AES #define HWCAP_AES (1<<3) #endif @@ -95,6 +99,13 @@ #define HWCAP2_SVEBITPERM (1 << 4) #endif +#ifndef HWCAP2_ECV +#define HWCAP2_ECV (1 << 19) +#endif + +#ifndef HWCAP2_WFXT +#define HWCAP2_WFXT (1u << 31) +#endif #ifndef PR_SVE_GET_VL // For old toolchains which do not have SVE related macros defined. #define PR_SVE_SET_VL 50 @@ -158,6 +169,12 @@ void VM_Version::get_os_cpu_info() { if (auxv2 & HWCAP2_SVEBITPERM) { set_feature(CPU_SVEBITPERM); } + if (auxv2 & HWCAP2_ECV) { + set_feature(CPU_ECV); + } + if (auxv2 & HWCAP2_WFXT) { + set_feature(CPU_WFXT); + } uint64_t ctr_el0; uint64_t dczid_el0; @@ -169,6 +186,12 @@ void VM_Version::get_os_cpu_info() { _icache_line_size = (1 << (ctr_el0 & 0x0f)) * 4; _dcache_line_size = (1 << ((ctr_el0 >> 16) & 0x0f)) * 4; + _cache_idc_enabled = ((ctr_el0 >> 28) & 0x1) != 0; + _cache_dic_enabled = ((ctr_el0 >> 29) & 0x1) != 0; + + // Probe whether IC IVAU is trapped. + // Must run before VM_Version::initialize() sets NeoverseN1ICacheErratumMitigation. + _ic_ivau_trapped = (ic_ivau_probe() == 1); if (!(dczid_el0 & 0x10)) { _zva_length = 4 << (dczid_el0 & 0xf); diff --git a/src/hotspot/os_cpu/windows_aarch64/prefetch_windows_aarch64.inline.hpp b/src/hotspot/os_cpu/windows_aarch64/prefetch_windows_aarch64.inline.hpp index a360ee342be..a2c8f0c685c 100644 --- a/src/hotspot/os_cpu/windows_aarch64/prefetch_windows_aarch64.inline.hpp +++ b/src/hotspot/os_cpu/windows_aarch64/prefetch_windows_aarch64.inline.hpp @@ -27,10 +27,24 @@ // Included in runtime/prefetch.inline.hpp +#include + +// __prefetch2(addr, prfop) emits a PRFM instruction. +// The prfop encoding is: +// type: PLD = 00, PLI = 01, PST = 10 +// target: L1 = 00, L2 = 01, L3 = 10 +// policy: KEEP = 0, STRM = 1 + inline void Prefetch::read (const void *loc, intx interval) { + if (interval >= 0) { + __prefetch2((const char*) loc + interval, /* PLD + L1 + KEEP */ 0); + } } inline void Prefetch::write(void *loc, intx interval) { + if (interval >= 0) { + __prefetch2((char*) loc + interval, /* PST + L1 + KEEP */ 16); + } } #endif // OS_CPU_WINDOWS_AARCH64_PREFETCH_WINDOWS_AARCH64_INLINE_HPP diff --git a/src/hotspot/os_cpu/windows_aarch64/sve_windows_aarch64.S b/src/hotspot/os_cpu/windows_aarch64/sve_windows_aarch64.S new file mode 100644 index 00000000000..e0c85830bd4 --- /dev/null +++ b/src/hotspot/os_cpu/windows_aarch64/sve_windows_aarch64.S @@ -0,0 +1,42 @@ +; +; Copyright (c) 2026, 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 get_sve_vector_length(); + ; + ; Returns the current SVE vector length in bytes. + ; This function uses the INCB instruction which increments a register + ; by the number of bytes in an SVE vector register. + ; + ; Note: This function will fault if SVE is not available or enabled. + ; The caller must ensure SVE support is detected before calling. + + ALIGN 4 + EXPORT get_sve_vector_length + AREA sve_text, CODE + +get_sve_vector_length + mov x0, #0 + incb x0 + ret + + END diff --git a/src/hotspot/os_cpu/windows_aarch64/vm_version_windows_aarch64.cpp b/src/hotspot/os_cpu/windows_aarch64/vm_version_windows_aarch64.cpp index 93beb549366..e78a37b4178 100644 --- a/src/hotspot/os_cpu/windows_aarch64/vm_version_windows_aarch64.cpp +++ b/src/hotspot/os_cpu/windows_aarch64/vm_version_windows_aarch64.cpp @@ -26,16 +26,19 @@ #include "runtime/os.hpp" #include "runtime/vm_version.hpp" +// Assembly function to get SVE vector length using INCB instruction +extern "C" int get_sve_vector_length(); + int VM_Version::get_current_sve_vector_length() { assert(VM_Version::supports_sve(), "should not call this"); - ShouldNotReachHere(); - return 0; + // Use assembly instruction to get the actual SVE vector length + return VM_Version::supports_sve() ? get_sve_vector_length() : 0; // This value is in bytes } int VM_Version::set_and_get_current_sve_vector_length(int length) { assert(VM_Version::supports_sve(), "should not call this"); - ShouldNotReachHere(); - return 0; + // Use assembly instruction to get the SVE vector length + return VM_Version::supports_sve() ? get_sve_vector_length() : 0; // This value is in bytes } void VM_Version::get_os_cpu_info() { @@ -47,11 +50,29 @@ void VM_Version::get_os_cpu_info() { set_feature(CPU_AES); set_feature(CPU_SHA1); set_feature(CPU_SHA2); + set_feature(CPU_PMULL); } if (IsProcessorFeaturePresent(PF_ARM_VFP_32_REGISTERS_AVAILABLE)) { set_feature(CPU_ASIMD); } - // No check for CPU_PMULL, CPU_SVE, CPU_SVE2 + if (IsProcessorFeaturePresent(PF_ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE)) { + set_feature(CPU_LSE); + } + if (IsProcessorFeaturePresent(PF_ARM_SVE_INSTRUCTIONS_AVAILABLE)) { + set_feature(CPU_SVE); + } + if (IsProcessorFeaturePresent(PF_ARM_SVE2_INSTRUCTIONS_AVAILABLE)) { + set_feature(CPU_SVE2); + } + if (IsProcessorFeaturePresent(PF_ARM_SVE_BITPERM_INSTRUCTIONS_AVAILABLE)) { + set_feature(CPU_SVEBITPERM); + } + if (IsProcessorFeaturePresent(PF_ARM_SHA3_INSTRUCTIONS_AVAILABLE)) { + set_feature(CPU_SHA3); + } + if (IsProcessorFeaturePresent(PF_ARM_SHA512_INSTRUCTIONS_AVAILABLE)) { + set_feature(CPU_SHA512); + } __int64 dczid_el0 = _ReadStatusReg(0x5807 /* ARM64_DCZID_EL0 */); @@ -102,8 +123,8 @@ void VM_Version::get_os_cpu_info() { SYSTEM_INFO si; GetSystemInfo(&si); _model = si.wProcessorLevel; - _variant = si.wProcessorRevision / 0xFF; - _revision = si.wProcessorRevision & 0xFF; + _variant = (si.wProcessorRevision >> 8) & 0xFF; // Variant is the upper byte of wProcessorRevision + _revision = si.wProcessorRevision & 0xFF; // Revision is the lower byte of wProcessorRevision } } } diff --git a/src/hotspot/os_cpu/windows_x86/prefetch_windows_x86.inline.hpp b/src/hotspot/os_cpu/windows_x86/prefetch_windows_x86.inline.hpp index 645fbe99a22..575eabc97dd 100644 --- a/src/hotspot/os_cpu/windows_x86/prefetch_windows_x86.inline.hpp +++ b/src/hotspot/os_cpu/windows_x86/prefetch_windows_x86.inline.hpp @@ -27,7 +27,18 @@ // Included in runtime/prefetch.inline.hpp -inline void Prefetch::read (const void *loc, intx interval) {} -inline void Prefetch::write(void *loc, intx interval) {} +#include + +inline void Prefetch::read (const void *loc, intx interval) { + if (interval >= 0) { + _mm_prefetch((const char*) loc + interval, _MM_HINT_T0); + } +} + +inline void Prefetch::write(void *loc, intx interval) { + if (interval >= 0) { + _mm_prefetch((const char*) loc + interval, _MM_HINT_T0); + } +} #endif // OS_CPU_WINDOWS_X86_PREFETCH_WINDOWS_X86_INLINE_HPP diff --git a/src/hotspot/share/adlc/formssel.cpp b/src/hotspot/share/adlc/formssel.cpp index 4dd2bff7c89..5802217c1c1 100644 --- a/src/hotspot/share/adlc/formssel.cpp +++ b/src/hotspot/share/adlc/formssel.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, 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 @@ -4233,11 +4233,13 @@ int MatchRule::is_expensive() const { strcmp(opType,"PopulateIndex")==0 || strcmp(opType,"AddReductionVI")==0 || strcmp(opType,"AddReductionVL")==0 || + strcmp(opType,"AddReductionVHF")==0 || strcmp(opType,"AddReductionVF")==0 || strcmp(opType,"AddReductionVD")==0 || strcmp(opType,"MulReductionVI")==0 || strcmp(opType,"MulReductionVL")==0 || strcmp(opType,"MulReductionVF")==0 || + strcmp(opType,"MulReductionVHF")==0 || strcmp(opType,"MulReductionVD")==0 || strcmp(opType,"MinReductionV")==0 || strcmp(opType,"MaxReductionV")==0 || @@ -4348,9 +4350,9 @@ bool MatchRule::is_vector() const { "MaxV", "MinV", "MinVHF", "MaxVHF", "UMinV", "UMaxV", "CompressV", "ExpandV", "CompressM", "CompressBitsV", "ExpandBitsV", "AddReductionVI", "AddReductionVL", - "AddReductionVF", "AddReductionVD", + "AddReductionVHF", "AddReductionVF", "AddReductionVD", "MulReductionVI", "MulReductionVL", - "MulReductionVF", "MulReductionVD", + "MulReductionVHF", "MulReductionVF", "MulReductionVD", "MaxReductionV", "MinReductionV", "AndReductionV", "OrReductionV", "XorReductionV", "MulAddVS2VI", "MacroLogicV", diff --git a/src/hotspot/share/asm/codeBuffer.cpp b/src/hotspot/share/asm/codeBuffer.cpp index ba525588f32..c6475050592 100644 --- a/src/hotspot/share/asm/codeBuffer.cpp +++ b/src/hotspot/share/asm/codeBuffer.cpp @@ -32,7 +32,6 @@ #include "oops/methodCounters.hpp" #include "oops/methodData.hpp" #include "oops/oop.inline.hpp" -#include "runtime/icache.hpp" #include "runtime/safepointVerifiers.hpp" #include "utilities/align.hpp" #include "utilities/copy.hpp" @@ -418,7 +417,7 @@ void CodeSection::expand_locs(int new_capacity) { new_capacity = old_capacity * 2; relocInfo* locs_start; if (_locs_own) { - locs_start = REALLOC_RESOURCE_ARRAY(relocInfo, _locs_start, old_capacity, new_capacity); + locs_start = REALLOC_RESOURCE_ARRAY(_locs_start, old_capacity, new_capacity); } else { locs_start = NEW_RESOURCE_ARRAY(relocInfo, new_capacity); Copy::conjoint_jbytes(_locs_start, locs_start, old_capacity * sizeof(relocInfo)); @@ -745,9 +744,6 @@ void CodeBuffer::copy_code_to(CodeBlob* dest_blob) { // Done moving code bytes; were they the right size? assert((int)align_up(dest.total_content_size(), oopSize) == dest_blob->content_size(), "sanity"); - - // Flush generated code - ICache::invalidate_range(dest_blob->code_begin(), dest_blob->code_size()); } // Move all my code into another code buffer. Consult applicable @@ -862,6 +858,13 @@ csize_t CodeBuffer::figure_expanded_capacities(CodeSection* which_cs, } void CodeBuffer::expand(CodeSection* which_cs, csize_t amount) { +#ifdef ASSERT + // The code below copies contents across temp buffers. The following + // sizes relate to buffer contents, and should not be changed by buffer + // expansion. + int old_total_skipped = total_skipped_instructions_size(); +#endif + #ifndef PRODUCT if (PrintNMethods && (WizardMode || Verbose)) { tty->print("expanding CodeBuffer:"); @@ -920,6 +923,7 @@ void CodeBuffer::expand(CodeSection* which_cs, csize_t amount) { assert(cb_sect->capacity() >= new_capacity[n], "big enough"); address cb_start = cb_sect->start(); cb_sect->set_end(cb_start + this_sect->size()); + cb_sect->register_skipped(this_sect->_skipped_instructions_size); if (this_sect->mark() == nullptr) { cb_sect->clear_mark(); } else { @@ -956,6 +960,9 @@ void CodeBuffer::expand(CodeSection* which_cs, csize_t amount) { this->print_on(tty); } #endif //PRODUCT + + assert(old_total_skipped == total_skipped_instructions_size(), + "Should match: %d == %d", old_total_skipped, total_skipped_instructions_size()); } void CodeBuffer::adjust_internal_address(address from, address to) { @@ -1140,7 +1147,7 @@ void AsmRemarks::clear() { uint AsmRemarks::print(uint offset, outputStream* strm) const { uint count = 0; const char* prefix = " ;; "; - const char* remstr = _remarks->lookup(offset); + const char* remstr = (_remarks ? _remarks->lookup(offset) : nullptr); while (remstr != nullptr) { strm->bol(); strm->print("%s", prefix); diff --git a/src/hotspot/share/c1/c1_LIRGenerator.hpp b/src/hotspot/share/c1/c1_LIRGenerator.hpp index ec0ea5dc047..8e30d05af6d 100644 --- a/src/hotspot/share/c1/c1_LIRGenerator.hpp +++ b/src/hotspot/share/c1/c1_LIRGenerator.hpp @@ -330,8 +330,9 @@ class LIRGenerator: public InstructionVisitor, public BlockClosure { // volatile field operations are never patchable because a klass // must be loaded to know it's volatile which means that the offset - // it always known as well. + // is always known as well. void volatile_field_store(LIR_Opr value, LIR_Address* address, CodeEmitInfo* info); + // volatile_field_load provides trailing membar semantics void volatile_field_load(LIR_Address* address, LIR_Opr result, CodeEmitInfo* info); void put_Object_unsafe(LIR_Opr src, LIR_Opr offset, LIR_Opr data, BasicType type, bool is_volatile); diff --git a/src/hotspot/share/c1/c1_Runtime1.cpp b/src/hotspot/share/c1/c1_Runtime1.cpp index 63764dd113a..38f563935e0 100644 --- a/src/hotspot/share/c1/c1_Runtime1.cpp +++ b/src/hotspot/share/c1/c1_Runtime1.cpp @@ -278,11 +278,9 @@ bool Runtime1::initialize(BufferBlob* blob) { if (!generate_blob_for(blob, id)) { return false; } - if (id == StubId::c1_forward_exception_id) { - // publish early c1 stubs at this point so later stubs can refer to them - AOTCodeCache::init_early_c1_table(); - } } + // disallow any further c1 stub generation + AOTCodeCache::set_c1_stubs_complete(); // printing #ifndef PRODUCT if (PrintSimpleStubs) { diff --git a/src/hotspot/share/cds/aotArtifactFinder.cpp b/src/hotspot/share/cds/aotArtifactFinder.cpp index f85f1e46520..bd69b18a1aa 100644 --- a/src/hotspot/share/cds/aotArtifactFinder.cpp +++ b/src/hotspot/share/cds/aotArtifactFinder.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, 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 @@ -32,6 +32,7 @@ #include "cds/lambdaProxyClassDictionary.hpp" #include "cds/regeneratedClasses.hpp" #include "classfile/systemDictionaryShared.hpp" +#include "classfile/vmClasses.hpp" #include "logging/log.hpp" #include "memory/metaspaceClosure.hpp" #include "oops/instanceKlass.hpp" @@ -169,6 +170,7 @@ void AOTArtifactFinder::find_artifacts() { end_scanning_for_oops(); TrainingData::cleanup_training_data(); + check_critical_classes(); } void AOTArtifactFinder::start_scanning_for_oops() { @@ -233,6 +235,7 @@ void AOTArtifactFinder::add_cached_instance_class(InstanceKlass* ik) { bool created; _seen_classes->put_if_absent(ik, &created); if (created) { + check_critical_class(ik); append_to_all_cached_classes(ik); // All super types must be added. @@ -310,3 +313,25 @@ void AOTArtifactFinder::all_cached_classes_do(MetaspaceClosure* it) { it->push(_all_cached_classes->adr_at(i)); } } + +void AOTArtifactFinder::check_critical_classes() { + if (CDSConfig::is_dumping_static_archive()) { + // vmClasses are store in the AOT cache (or AOT config file, or static archive). + // If any of the vmClasses is excluded, (usually due to incompatible JVMTI agent), + // the resulting cache/config/archive is unusable. + for (auto id : EnumRange{}) { + check_critical_class(vmClasses::klass_at(id)); + } + } +} + +void AOTArtifactFinder::check_critical_class(InstanceKlass* ik) { + if (SystemDictionaryShared::is_excluded_class(ik)) { + ResourceMark rm; + const char* msg = err_msg("Critical class %s has been excluded. %s cannot be written.", + ik->external_name(), + CDSConfig::type_of_archive_being_written()); + AOTMetaspace::unrecoverable_writing_error(msg); + } +} + diff --git a/src/hotspot/share/cds/aotArtifactFinder.hpp b/src/hotspot/share/cds/aotArtifactFinder.hpp index 05bcde6b0ac..50057b6caee 100644 --- a/src/hotspot/share/cds/aotArtifactFinder.hpp +++ b/src/hotspot/share/cds/aotArtifactFinder.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, 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 @@ -81,12 +81,14 @@ class AOTArtifactFinder : AllStatic { static void add_cached_type_array_class(TypeArrayKlass* tak); static void add_cached_instance_class(InstanceKlass* ik); static void append_to_all_cached_classes(Klass* k); + static void check_critical_class(InstanceKlass* ik); public: static void initialize(); static void find_artifacts(); static void add_cached_class(Klass* k); static void add_aot_inited_class(InstanceKlass* ik); static void all_cached_classes_do(MetaspaceClosure* it); + static void check_critical_classes(); static void dispose(); }; diff --git a/src/hotspot/share/cds/aotClassInitializer.cpp b/src/hotspot/share/cds/aotClassInitializer.cpp index 06fc3af6f30..9ef96282aeb 100644 --- a/src/hotspot/share/cds/aotClassInitializer.cpp +++ b/src/hotspot/share/cds/aotClassInitializer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, 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 @@ -59,6 +59,39 @@ bool AOTClassInitializer::can_archive_initialized_mirror(InstanceKlass* ik) { return false; } +#ifdef ASSERT + // If code in ik is executed, then ik must be in the state of being_initialized or + // fully_initialized. + // + // Check that no user code is executed during the assembly phase. Otherwise the user + // code may introduce undesirable environment dependencies into the heap image. + // If any of these two flags are set, we allow user code to be executed + // in the assembly phase. Note that these flags are strictly for the purpose + // of testing HotSpot and are not available in product builds. + if (AOTInitTestClass == nullptr && ArchiveHeapTestClass == nullptr) { + if (ik->defined_by_boot_loader()) { + // We allow boot classes to be AOT-initialized, except for classes from + // -Xbootclasspath (cp index >= 1) be AOT-initialized, as such classes may be + // provided by the user application. + assert(ik->shared_classpath_index() <= 0, + "only boot classed loaded from the modules image can be AOT-initialized"); + } else { + assert(ik->defined_by_platform_loader() || ik->defined_by_app_loader(), + "cannot AOT-initialized classed loaded by other loaders"); + + // Hidden classes from platform/app loaders need to be AOT-initialized to + // support AOT-linking of lambdas. These hidden classes are generated by the + // VM and do not contain user code. + if (!ik->is_hidden()) { + // OK: ik is an interface used by a lambda. When AOT-linking lambdas, we only + // support interfaces that are not interface_needs_clinit_execution_as_super(). + // See AOTConstantPoolResolver::check_lambda_metafactory_signature(). + assert(ik->is_interface() && !ik->interface_needs_clinit_execution_as_super(), "cannot execute Java code in assembly phase"); + } + } + } +#endif // ASSERT + // About "static field that may hold a different value" errors: // // Automatic selection for aot-inited classes @@ -234,7 +267,8 @@ bool AOTClassInitializer::can_archive_initialized_mirror(InstanceKlass* ik) { } void AOTClassInitializer::call_runtime_setup(JavaThread* current, InstanceKlass* ik) { - assert(ik->has_aot_initialized_mirror(), "sanity"); + precond(ik->has_aot_initialized_mirror()); + precond(!AOTLinkedClassBulkLoader::is_initializing_classes_early()); if (ik->is_runtime_setup_required()) { if (log_is_enabled(Info, aot, init)) { ResourceMark rm; diff --git a/src/hotspot/share/cds/aotGrowableArray.hpp b/src/hotspot/share/cds/aotGrowableArray.hpp deleted file mode 100644 index 0a0c137ed07..00000000000 --- a/src/hotspot/share/cds/aotGrowableArray.hpp +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2026, 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. - * - */ - -#ifndef SHARE_AOT_AOTGROWABLEARRAY_HPP -#define SHARE_AOT_AOTGROWABLEARRAY_HPP - -#include -#include - -class AOTGrowableArrayHelper { -public: - static void deallocate(void* mem); -}; - -// An AOTGrowableArray provides the same functionality as a GrowableArray that -// uses the C heap allocator. In addition, AOTGrowableArray can be iterated with -// MetaspaceClosure. This type should be used for growable arrays that need to be -// stored in the AOT cache. See ModuleEntry::_reads for an example. -template -class AOTGrowableArray : public GrowableArrayWithAllocator> { - friend class VMStructs; - friend class GrowableArrayWithAllocator; - - static E* allocate(int max, MemTag mem_tag) { - return (E*)GrowableArrayCHeapAllocator::allocate(max, sizeof(E), mem_tag); - } - - E* allocate() { - return allocate(this->_capacity, mtClass); - } - - void deallocate(E* mem) { -#if INCLUDE_CDS - AOTGrowableArrayHelper::deallocate(mem); -#else - GrowableArrayCHeapAllocator::deallocate(mem); -#endif - } - -public: - AOTGrowableArray(int initial_capacity, MemTag mem_tag) : - GrowableArrayWithAllocator( - allocate(initial_capacity, mem_tag), - initial_capacity) {} - - AOTGrowableArray() : AOTGrowableArray(0, mtClassShared) {} - - // methods required by MetaspaceClosure - void metaspace_pointers_do(MetaspaceClosure* it); - int size_in_heapwords() const { return (int)heap_word_size(sizeof(*this)); } - MetaspaceClosureType type() const { return MetaspaceClosureType::GrowableArrayType; } - static bool is_read_only_by_default() { return false; } -}; - -#endif // SHARE_AOT_AOTGROWABLEARRAY_HPP diff --git a/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp b/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp index 3653f9d518c..8129e6a5a81 100644 --- a/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp +++ b/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, 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 @@ -116,11 +116,24 @@ void AOTLinkedClassBulkLoader::preload_classes_in_table(Array* c } } +#ifdef ASSERT +// true iff we are inside AOTLinkedClassBulkLoader::link_classes(), when +// we are moving classes into the fully_initialized state before the +// JVM is able to execute any bytecodes. +static bool _is_initializing_classes_early = false; +bool AOTLinkedClassBulkLoader::is_initializing_classes_early() { + return _is_initializing_classes_early; +} +#endif + // Some cached heap objects may hold references to methods in aot-linked // classes (via MemberName). We need to make sure all classes are // linked before executing any bytecode. void AOTLinkedClassBulkLoader::link_classes(JavaThread* current) { + DEBUG_ONLY(_is_initializing_classes_early = true); link_classes_impl(current); + DEBUG_ONLY(_is_initializing_classes_early = false); + if (current->has_pending_exception()) { exit_on_exception(current); } @@ -135,6 +148,13 @@ void AOTLinkedClassBulkLoader::link_classes_impl(TRAPS) { link_classes_in_table(table->boot2(), CHECK); link_classes_in_table(table->platform(), CHECK); link_classes_in_table(table->app(), CHECK); + + init_classes_for_loader(Handle(), AOTLinkedClassTable::get()->boot1(), /*early_only=*/true, CHECK); + init_classes_for_loader(Handle(), AOTLinkedClassTable::get()->boot2(), /*early_only=*/true, CHECK); + init_classes_for_loader(Handle(), AOTLinkedClassTable::get()->platform(), /*early_only=*/true, CHECK); + init_classes_for_loader(Handle(), AOTLinkedClassTable::get()->app(), /*early_only=*/true, CHECK); + + log_info(aot, init)("------ finished early class init"); } void AOTLinkedClassBulkLoader::link_classes_in_table(Array* classes, TRAPS) { @@ -216,7 +236,7 @@ void AOTLinkedClassBulkLoader::validate_module(Klass* k, const char* category_na #endif void AOTLinkedClassBulkLoader::init_javabase_classes(JavaThread* current) { - init_classes_for_loader(Handle(), AOTLinkedClassTable::get()->boot1(), current); + init_classes_for_loader(Handle(), AOTLinkedClassTable::get()->boot1(), /*early_only=*/false, current); if (current->has_pending_exception()) { exit_on_exception(current); } @@ -246,9 +266,9 @@ void AOTLinkedClassBulkLoader::init_non_javabase_classes_impl(TRAPS) { assert(h_system_loader() != nullptr, "must be"); AOTLinkedClassTable* table = AOTLinkedClassTable::get(); - init_classes_for_loader(Handle(), table->boot2(), CHECK); - init_classes_for_loader(h_platform_loader, table->platform(), CHECK); - init_classes_for_loader(h_system_loader, table->app(), CHECK); + init_classes_for_loader(Handle(), table->boot2(), /*early_only=*/false, CHECK); + init_classes_for_loader(h_platform_loader, table->platform(), /*early_only=*/false, CHECK); + init_classes_for_loader(h_system_loader, table->app(), /*early_only=*/false, CHECK); if (Universe::is_fully_initialized() && VerifyDuringStartup) { // Make sure we're still in a clean state. @@ -260,6 +280,10 @@ void AOTLinkedClassBulkLoader::init_non_javabase_classes_impl(TRAPS) { tty->print_cr("==================== archived_training_data ** after all classes preloaded ===================="); TrainingData::print_archived_training_data_on(tty); } + LogStreamHandle(Info, aot, training, data) log; + if (log.is_enabled()) { + TrainingData::print_archived_training_data_on(&log); + } } // For the AOT cache to function properly, all classes in the AOTLinkedClassTable @@ -324,22 +348,80 @@ void AOTLinkedClassBulkLoader::initiate_loading(JavaThread* current, const char* } } -// Some AOT-linked classes for must be initialized early. This includes -// - classes that were AOT-initialized by AOTClassInitializer -// - the classes of all objects that are reachable from the archived mirrors of -// the AOT-linked classes for . -void AOTLinkedClassBulkLoader::init_classes_for_loader(Handle class_loader, Array* classes, TRAPS) { +// Can we move ik into fully_initialized state before the JVM is able to execute +// bytecodes? +static bool is_early_init_possible(InstanceKlass* ik) { + if (ik->is_runtime_setup_required()) { + // Bytecodes need to be executed in order to initialize this class. + if (log_is_enabled(Debug, aot, init)) { + ResourceMark rm; + log_debug(aot, init)("No early init %s: needs runtimeSetup()", + ik->external_name()); + } + return false; + } + + if (ik->super() != nullptr && !ik->super()->is_initialized()) { + // is_runtime_setup_required() == true for a super type + if (log_is_enabled(Debug, aot, init)) { + ResourceMark rm; + log_debug(aot, init)("No early init %s: super type %s not initialized", + ik->external_name(), ik->super()->external_name()); + } + return false; + } + + Array* interfaces = ik->local_interfaces(); + int num_interfaces = interfaces->length(); + for (int i = 0; i < num_interfaces; i++) { + InstanceKlass* intf = interfaces->at(i); + if (!intf->is_initialized() && intf->interface_needs_clinit_execution_as_super(/*also_check_supers*/false)) { + // is_runtime_setup_required() == true for this interface + if (log_is_enabled(Debug, aot, init)) { + ResourceMark rm; + log_debug(aot, init)("No early init %s: interface type %s not initialized", + ik->external_name(), intf->external_name()); + } + return false; + } + } + + return true; +} + +// Normally, classes are initialized on demand. However, some AOT-linked classes +// for the class_loader must be proactively intialized, including: +// - Classes that have an AOT-initialized mirror (they were AOT-initialized by +// AOTClassInitializer during the assembly phase). +// - The classes of all objects that are reachable from the archived mirrors of +// the AOT-linked classes for the class_loader. These are recorded in the special +// subgraph. +// +// (early_only == true) means that this function is called before the JVM +// is capable of executing Java bytecodes. +void AOTLinkedClassBulkLoader::init_classes_for_loader(Handle class_loader, Array* classes, + bool early_only, TRAPS) { if (classes != nullptr) { for (int i = 0; i < classes->length(); i++) { InstanceKlass* ik = classes->at(i); assert(ik->class_loader_data() != nullptr, "must be"); - if (ik->has_aot_initialized_mirror()) { - ik->initialize_with_aot_initialized_mirror(CHECK); + + bool do_init = ik->has_aot_initialized_mirror(); + if (do_init && early_only && !is_early_init_possible(ik)) { + // ik will be proactively initialized later when init_classes_for_loader() + // is called again with (early_only == false). + do_init = false; + } + + if (do_init) { + ik->initialize_with_aot_initialized_mirror(early_only, CHECK); } } } - HeapShared::init_classes_for_special_subgraph(class_loader, CHECK); + if (!early_only) { + HeapShared::init_classes_for_special_subgraph(class_loader, CHECK); + } } void AOTLinkedClassBulkLoader::replay_training_at_init(Array* classes, TRAPS) { diff --git a/src/hotspot/share/cds/aotLinkedClassBulkLoader.hpp b/src/hotspot/share/cds/aotLinkedClassBulkLoader.hpp index 31fdac386fe..24ff61cea1e 100644 --- a/src/hotspot/share/cds/aotLinkedClassBulkLoader.hpp +++ b/src/hotspot/share/cds/aotLinkedClassBulkLoader.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, 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 @@ -56,7 +56,7 @@ class AOTLinkedClassBulkLoader : AllStatic { static void link_classes_impl(TRAPS); static void link_classes_in_table(Array* classes, TRAPS); static void init_non_javabase_classes_impl(TRAPS); - static void init_classes_for_loader(Handle class_loader, Array* classes, TRAPS); + static void init_classes_for_loader(Handle class_loader, Array* classes, bool early_only, TRAPS); static void replay_training_at_init(Array* classes, TRAPS) NOT_CDS_RETURN; #ifdef ASSERT @@ -73,8 +73,9 @@ public: static void init_javabase_classes(JavaThread* current) NOT_CDS_RETURN; static void init_non_javabase_classes(JavaThread* current) NOT_CDS_RETURN; static void exit_on_exception(JavaThread* current); - static void replay_training_at_init_for_preloaded_classes(TRAPS) NOT_CDS_RETURN; + + static bool is_initializing_classes_early() NOT_DEBUG({return false;}); }; #endif // SHARE_CDS_AOTLINKEDCLASSBULKLOADER_HPP diff --git a/src/hotspot/share/cds/aotMapLogger.cpp b/src/hotspot/share/cds/aotMapLogger.cpp index 98336ff9b1f..9f338826fd6 100644 --- a/src/hotspot/share/cds/aotMapLogger.cpp +++ b/src/hotspot/share/cds/aotMapLogger.cpp @@ -589,7 +589,6 @@ public: } Klass* real_klass() { - assert(UseCompressedClassPointers, "heap archiving requires UseCompressedClassPointers"); return _data._klass; } diff --git a/src/hotspot/share/cds/aotMappedHeapLoader.hpp b/src/hotspot/share/cds/aotMappedHeapLoader.hpp index 7c5ca1b1f9e..10f5ce3124f 100644 --- a/src/hotspot/share/cds/aotMappedHeapLoader.hpp +++ b/src/hotspot/share/cds/aotMappedHeapLoader.hpp @@ -54,7 +54,7 @@ public: // Can this VM map archived heap region? Currently only G1+compressed{oops,cp} static bool can_map() { - CDS_JAVA_HEAP_ONLY(return (UseG1GC && UseCompressedClassPointers);) + CDS_JAVA_HEAP_ONLY(return UseG1GC;) NOT_CDS_JAVA_HEAP(return false;) } diff --git a/src/hotspot/share/cds/aotMappedHeapWriter.cpp b/src/hotspot/share/cds/aotMappedHeapWriter.cpp index 3456c845938..8f810ef5244 100644 --- a/src/hotspot/share/cds/aotMappedHeapWriter.cpp +++ b/src/hotspot/share/cds/aotMappedHeapWriter.cpp @@ -450,7 +450,6 @@ int AOTMappedHeapWriter::filler_array_length(size_t fill_bytes) { } HeapWord* AOTMappedHeapWriter::init_filler_array_at_buffer_top(int array_length, size_t fill_bytes) { - assert(UseCompressedClassPointers, "Archived heap only supported for compressed klasses"); Klass* oak = Universe::objectArrayKlass(); // already relocated to point to archived klass HeapWord* mem = offset_to_buffered_address(_buffer_used); memset(mem, 0, fill_bytes); @@ -724,7 +723,6 @@ template void AOTMappedHeapWriter::mark_oop_pointer(T* buffered_add } void AOTMappedHeapWriter::update_header_for_requested_obj(oop requested_obj, oop src_obj, Klass* src_klass) { - assert(UseCompressedClassPointers, "Archived heap only supported for compressed klasses"); narrowKlass nk = ArchiveBuilder::current()->get_requested_narrow_klass(src_klass); address buffered_addr = requested_addr_to_buffered_addr(cast_from_oop
(requested_obj)); diff --git a/src/hotspot/share/cds/aotMetaspace.cpp b/src/hotspot/share/cds/aotMetaspace.cpp index 76c37194882..4c23ede9cb8 100644 --- a/src/hotspot/share/cds/aotMetaspace.cpp +++ b/src/hotspot/share/cds/aotMetaspace.cpp @@ -250,9 +250,9 @@ static bool shared_base_too_high(char* specified_base, char* aligned_base, size_ static char* compute_shared_base(size_t cds_max) { char* specified_base = (char*)SharedBaseAddress; size_t alignment = AOTMetaspace::core_region_alignment(); - if (UseCompressedClassPointers && CompressedKlassPointers::needs_class_space()) { - alignment = MAX2(alignment, Metaspace::reserve_alignment()); - } +#if INCLUDE_CLASS_SPACE + alignment = MAX2(alignment, Metaspace::reserve_alignment()); +#endif if (SharedBaseAddress == 0) { // Special meaning of -XX:SharedBaseAddress=0 -> Always map archive at os-selected address. @@ -949,11 +949,18 @@ void AOTMetaspace::dump_static_archive(TRAPS) { ResourceMark rm(THREAD); HandleMark hm(THREAD); - if (CDSConfig::is_dumping_final_static_archive() && AOTPrintTrainingInfo) { - tty->print_cr("==================== archived_training_data ** before dumping ===================="); - TrainingData::print_archived_training_data_on(tty); + if (CDSConfig::is_dumping_final_static_archive()) { + if (AOTPrintTrainingInfo) { + tty->print_cr("==================== archived_training_data ** before dumping ===================="); + TrainingData::print_archived_training_data_on(tty); + } + LogStreamHandle(Info, aot, training, data) log; + if (log.is_enabled()) { + TrainingData::print_archived_training_data_on(&log); + } } + StaticArchiveBuilder builder; dump_static_archive_impl(builder, THREAD); if (HAS_PENDING_EXCEPTION) { @@ -1637,32 +1644,29 @@ MapArchiveResult AOTMetaspace::map_archives(FileMapInfo* static_mapinfo, FileMap aot_log_debug(aot)("Failed to reserve spaces (use_requested_addr=%u)", (unsigned)use_requested_addr); } else { - if (Metaspace::using_class_space()) { - prot_zone_size = protection_zone_size(); - } + CLASS_SPACE_ONLY(prot_zone_size = protection_zone_size();) -#ifdef ASSERT // Some sanity checks after reserving address spaces for archives // and class space. assert(archive_space_rs.is_reserved(), "Sanity"); - if (Metaspace::using_class_space()) { - assert(archive_space_rs.base() == mapped_base_address && - archive_space_rs.size() > protection_zone_size(), - "Archive space must lead and include the protection zone"); - // Class space must closely follow the archive space. Both spaces - // must be aligned correctly. - assert(class_space_rs.is_reserved() && class_space_rs.size() > 0, - "A class space should have been reserved"); - assert(class_space_rs.base() >= archive_space_rs.end(), - "class space should follow the cds archive space"); - assert(is_aligned(archive_space_rs.base(), - core_region_alignment()), - "Archive space misaligned"); - assert(is_aligned(class_space_rs.base(), - Metaspace::reserve_alignment()), - "class space misaligned"); - } -#endif // ASSERT + +#if INCLUDE_CLASS_SPACE + assert(archive_space_rs.base() == mapped_base_address && + archive_space_rs.size() > protection_zone_size(), + "Archive space must lead and include the protection zone"); + // Class space must closely follow the archive space. Both spaces + // must be aligned correctly. + assert(class_space_rs.is_reserved() && class_space_rs.size() > 0, + "A class space should have been reserved"); + assert(class_space_rs.base() >= archive_space_rs.end(), + "class space should follow the cds archive space"); + assert(is_aligned(archive_space_rs.base(), + core_region_alignment()), + "Archive space misaligned"); + assert(is_aligned(class_space_rs.base(), + Metaspace::reserve_alignment()), + "class space misaligned"); +#endif // INCLUDE_CLASS_SPACE aot_log_info(aot)("Reserved archive_space_rs [" INTPTR_FORMAT " - " INTPTR_FORMAT "] (%zu) bytes%s", p2i(archive_space_rs.base()), p2i(archive_space_rs.end()), archive_space_rs.size(), @@ -1764,62 +1768,60 @@ MapArchiveResult AOTMetaspace::map_archives(FileMapInfo* static_mapinfo, FileMap if (result == MAP_ARCHIVE_SUCCESS) { SharedBaseAddress = (size_t)mapped_base_address; -#ifdef _LP64 - if (Metaspace::using_class_space()) { - assert(prot_zone_size > 0 && - *(mapped_base_address) == 'P' && - *(mapped_base_address + prot_zone_size - 1) == 'P', - "Protection zone was overwritten?"); - // Set up ccs in metaspace. - Metaspace::initialize_class_space(class_space_rs); +#if INCLUDE_CLASS_SPACE + assert(prot_zone_size > 0 && + *(mapped_base_address) == 'P' && + *(mapped_base_address + prot_zone_size - 1) == 'P', + "Protection zone was overwritten?"); + // Set up ccs in metaspace. + Metaspace::initialize_class_space(class_space_rs); - // Set up compressed Klass pointer encoding: the encoding range must - // cover both archive and class space. - const address klass_range_start = (address)mapped_base_address; - const size_t klass_range_size = (address)class_space_rs.end() - klass_range_start; - if (INCLUDE_CDS_JAVA_HEAP || UseCompactObjectHeaders) { - // The CDS archive may contain narrow Klass IDs that were precomputed at archive generation time: - // - every archived java object header (only if INCLUDE_CDS_JAVA_HEAP) - // - every archived Klass' prototype (only if +UseCompactObjectHeaders) - // - // In order for those IDs to still be valid, we need to dictate base and shift: base should be the - // mapping start (including protection zone), shift should be the shift used at archive generation time. - CompressedKlassPointers::initialize_for_given_encoding( - klass_range_start, klass_range_size, - klass_range_start, ArchiveBuilder::precomputed_narrow_klass_shift() // precomputed encoding, see ArchiveBuilder - ); - assert(CompressedKlassPointers::base() == klass_range_start, "must be"); - } else { - // Let JVM freely choose encoding base and shift - CompressedKlassPointers::initialize(klass_range_start, klass_range_size); - assert(CompressedKlassPointers::base() == nullptr || - CompressedKlassPointers::base() == klass_range_start, "must be"); - } - // Establish protection zone, but only if we need one - if (CompressedKlassPointers::base() == klass_range_start) { - CompressedKlassPointers::establish_protection_zone(klass_range_start, prot_zone_size); - } + // Set up compressed Klass pointer encoding: the encoding range must + // cover both archive and class space. + const address klass_range_start = (address)mapped_base_address; + const size_t klass_range_size = (address)class_space_rs.end() - klass_range_start; + if (INCLUDE_CDS_JAVA_HEAP || UseCompactObjectHeaders) { + // The CDS archive may contain narrow Klass IDs that were precomputed at archive generation time: + // - every archived java object header (only if INCLUDE_CDS_JAVA_HEAP) + // - every archived Klass' prototype (only if +UseCompactObjectHeaders) + // + // In order for those IDs to still be valid, we need to dictate base and shift: base should be the + // mapping start (including protection zone), shift should be the shift used at archive generation time. + CompressedKlassPointers::initialize_for_given_encoding( + klass_range_start, klass_range_size, + klass_range_start, ArchiveBuilder::precomputed_narrow_klass_shift() // precomputed encoding, see ArchiveBuilder + ); + assert(CompressedKlassPointers::base() == klass_range_start, "must be"); + } else { + // Let JVM freely choose encoding base and shift + CompressedKlassPointers::initialize(klass_range_start, klass_range_size); + assert(CompressedKlassPointers::base() == nullptr || + CompressedKlassPointers::base() == klass_range_start, "must be"); + } + // Establish protection zone, but only if we need one + if (CompressedKlassPointers::base() == klass_range_start) { + CompressedKlassPointers::establish_protection_zone(klass_range_start, prot_zone_size); + } - if (static_mapinfo->can_use_heap_region()) { - if (static_mapinfo->object_streaming_mode()) { - HeapShared::initialize_loading_mode(HeapArchiveMode::_streaming); - } else { - // map_or_load_heap_region() compares the current narrow oop and klass encodings - // with the archived ones, so it must be done after all encodings are determined. - static_mapinfo->map_or_load_heap_region(); - HeapShared::initialize_loading_mode(HeapArchiveMode::_mapping); - } + if (static_mapinfo->can_use_heap_region()) { + if (static_mapinfo->object_streaming_mode()) { + HeapShared::initialize_loading_mode(HeapArchiveMode::_streaming); } else { - FileMapRegion* r = static_mapinfo->region_at(AOTMetaspace::hp); - if (r->used() > 0) { - AOTMetaspace::report_loading_error("Cannot use CDS heap data."); - } - if (!CDSConfig::is_dumping_static_archive()) { - CDSConfig::stop_using_full_module_graph("No CDS heap data"); - } + // map_or_load_heap_region() compares the current narrow oop and klass encodings + // with the archived ones, so it must be done after all encodings are determined. + static_mapinfo->map_or_load_heap_region(); + HeapShared::initialize_loading_mode(HeapArchiveMode::_mapping); + } + } else { + FileMapRegion* r = static_mapinfo->region_at(AOTMetaspace::hp); + if (r->used() > 0) { + AOTMetaspace::report_loading_error("Cannot use CDS heap data."); + } + if (!CDSConfig::is_dumping_static_archive()) { + CDSConfig::stop_using_full_module_graph("No CDS heap data"); } } -#endif // _LP64 +#endif // INCLUDE_CLASS_SPACE log_info(aot)("initial optimized module handling: %s", CDSConfig::is_using_optimized_module_handling() ? "enabled" : "disabled"); log_info(aot)("initial full module graph: %s", CDSConfig::is_using_full_module_graph() ? "enabled" : "disabled"); } else { @@ -1852,8 +1854,13 @@ MapArchiveResult AOTMetaspace::map_archives(FileMapInfo* static_mapinfo, FileMap // (The gap may result from different alignment requirements between metaspace // and CDS) // -// If UseCompressedClassPointers is disabled, only one address space will be -// reserved: +// The range encompassing both spaces will be suitable to en/decode narrow Klass +// pointers: the base will be valid for encoding the range [Base, End) and not +// surpass the max. range for that encoding. +// +// On 32-bit, a "narrow" Klass is just the pointer itself, and the Klass encoding +// range encompasses the whole address range. Consequently, we can "decode" and +// "encode" any pointer anywhere, and so are free to place the CDS archive anywhere: // // +-- Base address End // | | @@ -1867,27 +1874,21 @@ MapArchiveResult AOTMetaspace::map_archives(FileMapInfo* static_mapinfo, FileMap // use_archive_base_addr address is false, this base address is determined // by the platform. // -// If UseCompressedClassPointers=1, the range encompassing both spaces will be -// suitable to en/decode narrow Klass pointers: the base will be valid for -// encoding, the range [Base, End) and not surpass the max. range for that encoding. -// // Return: // // - On success: // - total_space_rs will be reserved as whole for archive_space_rs and -// class_space_rs if UseCompressedClassPointers is true. +// class_space_rs on 64-bit. // On Windows, try reserve archive_space_rs and class_space_rs // separately first if use_archive_base_addr is true. // - archive_space_rs will be reserved and large enough to host static and // if needed dynamic archive: [Base, A). // archive_space_rs.base and size will be aligned to CDS reserve // granularity. -// - class_space_rs: If UseCompressedClassPointers=1, class_space_rs will -// be reserved. Its start address will be aligned to metaspace reserve -// alignment, which may differ from CDS alignment. It will follow the cds -// archive space, close enough such that narrow class pointer encoding -// covers both spaces. -// If UseCompressedClassPointers=0, class_space_rs remains unreserved. +// - class_space_rs: On 64-bit, class_space_rs will be reserved. Its start +// address will be aligned to metaspace reserve alignment, which may differ +// from CDS alignment. It will follow the cds archive space, close enough +// such that narrow class pointer encoding covers both spaces. // - On error: null is returned and the spaces remain unreserved. char* AOTMetaspace::reserve_address_space_for_archives(FileMapInfo* static_mapinfo, FileMapInfo* dynamic_mapinfo, @@ -1903,32 +1904,34 @@ char* AOTMetaspace::reserve_address_space_for_archives(FileMapInfo* static_mapin size_t archive_end_offset = (dynamic_mapinfo == nullptr) ? static_mapinfo->mapping_end_offset() : dynamic_mapinfo->mapping_end_offset(); size_t archive_space_size = align_up(archive_end_offset, archive_space_alignment); - if (!Metaspace::using_class_space()) { - // Get the simple case out of the way first: - // no compressed class space, simple allocation. +#if !INCLUDE_CLASS_SPACE - // When running without class space, requested archive base should be aligned to cds core alignment. - assert(is_aligned(base_address, archive_space_alignment), - "Archive base address unaligned: " PTR_FORMAT ", needs alignment: %zu.", - p2i(base_address), archive_space_alignment); + // Get the simple case out of the way first: + // no compressed class space, simple allocation. - archive_space_rs = MemoryReserver::reserve((char*)base_address, - archive_space_size, - archive_space_alignment, - os::vm_page_size(), - mtNone); - if (archive_space_rs.is_reserved()) { - assert(base_address == nullptr || - (address)archive_space_rs.base() == base_address, "Sanity"); - // Register archive space with NMT. - MemTracker::record_virtual_memory_tag(archive_space_rs, mtClassShared); - return archive_space_rs.base(); - } - return nullptr; + // When running without class space, requested archive base should be aligned to cds core alignment. + assert(is_aligned(base_address, archive_space_alignment), + "Archive base address unaligned: " PTR_FORMAT ", needs alignment: %zu.", + p2i(base_address), archive_space_alignment); + + archive_space_rs = MemoryReserver::reserve((char*)base_address, + archive_space_size, + archive_space_alignment, + os::vm_page_size(), + mtNone); + if (archive_space_rs.is_reserved()) { + assert(base_address == nullptr || + (address)archive_space_rs.base() == base_address, "Sanity"); + // Register archive space with NMT. + MemTracker::record_virtual_memory_tag(archive_space_rs, mtClassShared); + return archive_space_rs.base(); } -#ifdef _LP64 + return nullptr; +#else + + // INCLUDE_CLASS_SPACE=1 // Complex case: two spaces adjacent to each other, both to be addressable // with narrow class pointers. // We reserve the whole range spanning both spaces, then split that range up. @@ -2040,11 +2043,7 @@ char* AOTMetaspace::reserve_address_space_for_archives(FileMapInfo* static_mapin return archive_space_rs.base(); -#else - ShouldNotReachHere(); - return nullptr; -#endif - +#endif // INCLUDE_CLASS_SPACE } void AOTMetaspace::release_reserved_spaces(ReservedSpace& total_space_rs, diff --git a/src/hotspot/share/cds/aotStreamedHeapLoader.cpp b/src/hotspot/share/cds/aotStreamedHeapLoader.cpp index 39f735543cd..7f9f8cf0628 100644 --- a/src/hotspot/share/cds/aotStreamedHeapLoader.cpp +++ b/src/hotspot/share/cds/aotStreamedHeapLoader.cpp @@ -797,7 +797,7 @@ void AOTStreamedHeapLoader::cleanup() { Universe::vm_global()->release(&handles[num_null_handles], num_handles - num_null_handles); } - FREE_C_HEAP_ARRAY(void*, _object_index_to_heap_object_table); + FREE_C_HEAP_ARRAY(_object_index_to_heap_object_table); // Unmap regions FileMapInfo::current_info()->unmap_region(AOTMetaspace::hp); diff --git a/src/hotspot/share/cds/aotStreamedHeapWriter.cpp b/src/hotspot/share/cds/aotStreamedHeapWriter.cpp index ad363f21fdb..25bef10a673 100644 --- a/src/hotspot/share/cds/aotStreamedHeapWriter.cpp +++ b/src/hotspot/share/cds/aotStreamedHeapWriter.cpp @@ -369,7 +369,6 @@ template void AOTStreamedHeapWriter::map_oop_field_in_buffer(oop ob } void AOTStreamedHeapWriter::update_header_for_buffered_addr(address buffered_addr, oop src_obj, Klass* src_klass) { - assert(UseCompressedClassPointers, "Archived heap only supported for compressed klasses"); narrowKlass nk = ArchiveBuilder::current()->get_requested_narrow_klass(src_klass); markWord mw = markWord::prototype(); diff --git a/src/hotspot/share/cds/archiveBuilder.cpp b/src/hotspot/share/cds/archiveBuilder.cpp index b2d6600e44f..cf51897c2f1 100644 --- a/src/hotspot/share/cds/archiveBuilder.cpp +++ b/src/hotspot/share/cds/archiveBuilder.cpp @@ -1092,20 +1092,17 @@ class RelocateBufferToRequested : public BitMapClosure { } }; -#ifdef _LP64 int ArchiveBuilder::precomputed_narrow_klass_shift() { - // Legacy Mode: - // We use 32 bits for narrowKlass, which should cover the full 4G Klass range. Shift can be 0. + // Standard Mode: + // We use 32 bits for narrowKlass, which should cover a full 4G Klass range. Shift can be 0. // CompactObjectHeader Mode: // narrowKlass is much smaller, and we use the highest possible shift value to later get the maximum // Klass encoding range. // // Note that all of this may change in the future, if we decide to correct the pre-calculated // narrow Klass IDs at archive load time. - assert(UseCompressedClassPointers, "Only needed for compressed class pointers"); return UseCompactObjectHeaders ? CompressedKlassPointers::max_shift() : 0; } -#endif // _LP64 void ArchiveBuilder::relocate_to_requested() { if (!ro_region()->is_packed()) { @@ -1174,7 +1171,7 @@ void ArchiveBuilder::write_archive(FileMapInfo* mapinfo, AOTMappedHeapInfo* mapp AOTMapLogger::dumptime_log(this, mapinfo, mapped_heap_info, streamed_heap_info, bitmap, bitmap_size_in_bytes); } CDS_JAVA_HEAP_ONLY(HeapShared::destroy_archived_object_cache()); - FREE_C_HEAP_ARRAY(char, bitmap); + FREE_C_HEAP_ARRAY(bitmap); } void ArchiveBuilder::write_region(FileMapInfo* mapinfo, int region_idx, DumpRegion* dump_region, bool read_only, bool allow_exec) { diff --git a/src/hotspot/share/cds/archiveBuilder.hpp b/src/hotspot/share/cds/archiveBuilder.hpp index b3667ea11b4..6a9df87092b 100644 --- a/src/hotspot/share/cds/archiveBuilder.hpp +++ b/src/hotspot/share/cds/archiveBuilder.hpp @@ -484,7 +484,6 @@ public: void print_stats(); void report_out_of_space(const char* name, size_t needed_bytes); -#ifdef _LP64 // The CDS archive contains pre-computed narrow Klass IDs. It carries them in the headers of // archived heap objects. With +UseCompactObjectHeaders, it also carries them in prototypes // in Klass. @@ -504,7 +503,6 @@ public: // TinyClassPointer Mode: // We use the highest possible shift value to maximize the encoding range size. static int precomputed_narrow_klass_shift(); -#endif // _LP64 }; diff --git a/src/hotspot/share/cds/archiveUtils.cpp b/src/hotspot/share/cds/archiveUtils.cpp index f79b1e134e8..6e0608e196b 100644 --- a/src/hotspot/share/cds/archiveUtils.cpp +++ b/src/hotspot/share/cds/archiveUtils.cpp @@ -360,8 +360,8 @@ char* DumpRegion::allocate_metaspace_obj(size_t num_bytes, address src, Metaspac bool is_instance_class = is_class && ((Klass*)src)->is_instance_klass(); #ifdef _LP64 - // More strict alignments needed for UseCompressedClassPointers - if (is_class && UseCompressedClassPointers) { + // More strict alignments needed for Klass objects + if (is_class) { size_t klass_alignment = checked_cast(nth_bit(ArchiveBuilder::precomputed_narrow_klass_shift())); alignment = MAX2(alignment, klass_alignment); precond(is_aligned(alignment, SharedSpaceObjectAlignment)); diff --git a/src/hotspot/share/cds/archiveUtils.hpp b/src/hotspot/share/cds/archiveUtils.hpp index 84ad8e6bdf3..42455adedd0 100644 --- a/src/hotspot/share/cds/archiveUtils.hpp +++ b/src/hotspot/share/cds/archiveUtils.hpp @@ -36,6 +36,8 @@ #include "runtime/semaphore.hpp" #include "utilities/bitMap.hpp" #include "utilities/exceptions.hpp" +#include "utilities/growableArray.hpp" +#include "utilities/hashTable.hpp" #include "utilities/macros.hpp" class BootstrapInfo; @@ -44,7 +46,6 @@ class ReservedSpace; class VirtualSpace; template class Array; -template class GrowableArray; // ArchivePtrMarker is used to mark the location of pointers embedded in a CDS archive. E.g., when an // InstanceKlass k is dumped, we mark the location of the k->_name pointer by effectively calling @@ -401,4 +402,39 @@ public: void run_task(ArchiveWorkerTask* task); }; +// A utility class for writing an array of unique items into the +// AOT cache. For determinism, the order of the array is the same +// as calls to add(). I.e., if items are added in the order +// of A, B, A, C, B, D, then the array will be written as {A, B, C, D} +template +class ArchivableTable : public AnyObj { + using Table = HashTable; + Table* _seen_items; + GrowableArray* _ordered_array; +public: + ArchivableTable() { + _seen_items = new (mtClassShared)Table(); + _ordered_array = new (mtClassShared)GrowableArray(128, mtClassShared); + } + + ~ArchivableTable() { + delete _seen_items; + delete _ordered_array; + } + + void add(T t) { + bool created; + _seen_items->put_if_absent(t, &created); + if (created) { + _ordered_array->append(t); + } + } + + Array* write_ordered_array() { + return ArchiveUtils::archive_array(_ordered_array); + } +}; + +using ArchivableKlassTable = ArchivableTable; + #endif // SHARE_CDS_ARCHIVEUTILS_HPP diff --git a/src/hotspot/share/cds/cdsConfig.cpp b/src/hotspot/share/cds/cdsConfig.cpp index 9d191bf0121..21066f76932 100644 --- a/src/hotspot/share/cds/cdsConfig.cpp +++ b/src/hotspot/share/cds/cdsConfig.cpp @@ -520,7 +520,7 @@ static void substitute_aot_filename(JVMFlagsEnum flag_enum) { JVMFlag::Error err = JVMFlagAccess::set_ccstr(flag, &new_filename, JVMFlagOrigin::ERGONOMIC); assert(err == JVMFlag::SUCCESS, "must never fail"); } - FREE_C_HEAP_ARRAY(char, new_filename); + FREE_C_HEAP_ARRAY(new_filename); } void CDSConfig::check_aotmode_record() { @@ -891,10 +891,6 @@ const char* CDSConfig::type_of_archive_being_written() { // If an incompatible VM options is found, return a text message that explains why static const char* check_options_incompatible_with_dumping_heap() { #if INCLUDE_CDS_JAVA_HEAP - if (!UseCompressedClassPointers) { - return "UseCompressedClassPointers must be true"; - } - return nullptr; #else return "JVM not configured for writing Java heap objects"; diff --git a/src/hotspot/share/cds/classListWriter.cpp b/src/hotspot/share/cds/classListWriter.cpp index 8e1f298e8e3..c90e233df73 100644 --- a/src/hotspot/share/cds/classListWriter.cpp +++ b/src/hotspot/share/cds/classListWriter.cpp @@ -49,7 +49,7 @@ void ClassListWriter::init() { _classlist_file->print_cr("# This file is generated via the -XX:DumpLoadedClassList= option"); _classlist_file->print_cr("# and is used at CDS archive dump time (see -Xshare:dump)."); _classlist_file->print_cr("#"); - FREE_C_HEAP_ARRAY(char, list_name); + FREE_C_HEAP_ARRAY(list_name); } } diff --git a/src/hotspot/share/cds/cppVtables.cpp b/src/hotspot/share/cds/cppVtables.cpp index dc5a777d7b1..57da12dee48 100644 --- a/src/hotspot/share/cds/cppVtables.cpp +++ b/src/hotspot/share/cds/cppVtables.cpp @@ -22,7 +22,6 @@ * */ -#include "cds/aotGrowableArray.hpp" #include "cds/aotMetaspace.hpp" #include "cds/archiveBuilder.hpp" #include "cds/archiveUtils.hpp" @@ -41,6 +40,7 @@ #include "oops/typeArrayKlass.hpp" #include "runtime/arguments.hpp" #include "utilities/globalDefinitions.hpp" +#include "utilities/growableArray.hpp" // Objects of the Metadata types (such as Klass and ConstantPool) have C++ vtables. // (In GCC this is the field ::_vptr, i.e., first word in the object.) @@ -58,10 +58,10 @@ #ifndef PRODUCT -// AOTGrowableArray has a vtable only when in non-product builds (due to +// GrowableArray has a vtable only when in non-product builds (due to // the virtual printing functions in AnyObj). -using GrowableArray_ModuleEntry_ptr = AOTGrowableArray; +using GrowableArray_ModuleEntry_ptr = GrowableArray; #define DEBUG_CPP_VTABLE_TYPES_DO(f) \ f(GrowableArray_ModuleEntry_ptr) \ diff --git a/src/hotspot/share/cds/filemap.cpp b/src/hotspot/share/cds/filemap.cpp index a07f13cefca..186dca4fa13 100644 --- a/src/hotspot/share/cds/filemap.cpp +++ b/src/hotspot/share/cds/filemap.cpp @@ -225,15 +225,9 @@ void FileMapHeader::populate(FileMapInfo *info, size_t core_region_alignment, } #endif _compressed_oops = UseCompressedOops; - _compressed_class_ptrs = UseCompressedClassPointers; - if (UseCompressedClassPointers) { -#ifdef _LP64 - _narrow_klass_pointer_bits = CompressedKlassPointers::narrow_klass_pointer_bits(); - _narrow_klass_shift = ArchiveBuilder::precomputed_narrow_klass_shift(); -#endif - } else { - _narrow_klass_pointer_bits = _narrow_klass_shift = -1; - } + _narrow_klass_pointer_bits = CompressedKlassPointers::narrow_klass_pointer_bits(); + _narrow_klass_shift = ArchiveBuilder::precomputed_narrow_klass_shift(); + // Which JIT compier is used _compiler_type = (u1)CompilerConfig::compiler_type(); _type_profile_level = TypeProfileLevel; @@ -295,7 +289,6 @@ void FileMapHeader::print(outputStream* st) { st->print_cr("- max_heap_size: %zu", _max_heap_size); st->print_cr("- narrow_oop_mode: %d", _narrow_oop_mode); st->print_cr("- compressed_oops: %d", _compressed_oops); - st->print_cr("- compressed_class_ptrs: %d", _compressed_class_ptrs); st->print_cr("- narrow_klass_pointer_bits: %d", _narrow_klass_pointer_bits); st->print_cr("- narrow_klass_shift: %d", _narrow_klass_shift); st->print_cr("- cloned_vtables: %u", cast_to_u4(_cloned_vtables)); @@ -409,7 +402,7 @@ public: ~FileHeaderHelper() { if (_header != nullptr) { - FREE_C_HEAP_ARRAY(char, _header); + FREE_C_HEAP_ARRAY(_header); } if (_fd != -1) { ::close(_fd); @@ -1926,11 +1919,12 @@ bool FileMapHeader::validate() { _has_platform_or_app_classes = false; } - aot_log_info(aot)("The %s was created with UseCompressedOops = %d, UseCompressedClassPointers = %d, UseCompactObjectHeaders = %d", - file_type, compressed_oops(), compressed_class_pointers(), compact_headers()); - if (compressed_oops() != UseCompressedOops || compressed_class_pointers() != UseCompressedClassPointers) { - aot_log_warning(aot)("Unable to use %s.\nThe saved state of UseCompressedOops and UseCompressedClassPointers is " - "different from runtime, CDS will be disabled.", file_type); + aot_log_info(aot)("The %s was created with UseCompressedOops = %d, UseCompactObjectHeaders = %d", + file_type, compressed_oops(), compact_headers()); + if (compressed_oops() != UseCompressedOops) { + aot_log_warning(aot)("Unable to use %s.\nThe saved state of UseCompressedOops (%d) is " + "different from runtime (%d), CDS will be disabled.", file_type, + compressed_oops(), UseCompressedOops); return false; } diff --git a/src/hotspot/share/cds/filemap.hpp b/src/hotspot/share/cds/filemap.hpp index 56b88df378a..bae08bd5bc7 100644 --- a/src/hotspot/share/cds/filemap.hpp +++ b/src/hotspot/share/cds/filemap.hpp @@ -120,7 +120,6 @@ private: CompressedOops::Mode _narrow_oop_mode; // compressed oop encoding mode bool _object_streaming_mode; // dump was created for object streaming bool _compressed_oops; // save the flag UseCompressedOops - bool _compressed_class_ptrs; // save the flag UseCompressedClassPointers int _narrow_klass_pointer_bits; // save number of bits in narrowKlass int _narrow_klass_shift; // save shift width used to pre-compute narrowKlass IDs in archived heap objects narrowPtr _cloned_vtables; // The address of the first cloned vtable @@ -200,7 +199,6 @@ public: bool has_platform_or_app_classes() const { return _has_platform_or_app_classes; } bool has_aot_linked_classes() const { return _has_aot_linked_classes; } bool compressed_oops() const { return _compressed_oops; } - bool compressed_class_pointers() const { return _compressed_class_ptrs; } int narrow_klass_pointer_bits() const { return _narrow_klass_pointer_bits; } int narrow_klass_shift() const { return _narrow_klass_shift; } bool has_full_module_graph() const { return _has_full_module_graph; } diff --git a/src/hotspot/share/cds/heapShared.cpp b/src/hotspot/share/cds/heapShared.cpp index 708a19e9a7d..d75816656b0 100644 --- a/src/hotspot/share/cds/heapShared.cpp +++ b/src/hotspot/share/cds/heapShared.cpp @@ -112,6 +112,11 @@ static Klass* _test_class = nullptr; static const ArchivedKlassSubGraphInfoRecord* _test_class_record = nullptr; #endif +#ifdef ASSERT +// All classes that have at least one instance in the cached heap. +static ArchivableKlassTable* _dumptime_classes_with_cached_oops = nullptr; +static Array* _runtime_classes_with_cached_oops = nullptr; +#endif // // If you add new entries to the following tables, you should know what you're doing! @@ -391,6 +396,21 @@ void HeapShared::initialize_streaming() { } void HeapShared::enable_gc() { +#ifdef ASSERT + // At this point, a GC may start and will be able to see some or all + // of the cached oops. The class of each oop seen by the GC must have + // already been loaded. One function with such a requirement is + // ClaimMetadataVisitingOopIterateClosure::do_klass(). + if (is_archived_heap_in_use()) { + Array* klasses = _runtime_classes_with_cached_oops; + + for (int i = 0; i < klasses->length(); i++) { + assert(klasses->at(i)->class_loader_data() != nullptr, + "class of cached oop must have been loaded"); + } + } +#endif + if (AOTStreamedHeapLoader::is_in_use()) { AOTStreamedHeapLoader::enable_gc(); } @@ -564,8 +584,10 @@ bool HeapShared::archive_object(oop obj, oop referrer, KlassSubGraphInfo* subgra return false; } + AOTArtifactFinder::add_cached_class(obj->klass()); AOTOopChecker::check(obj); // Make sure contents of this oop are safe. count_allocation(obj->size()); + DEBUG_ONLY(_dumptime_classes_with_cached_oops->add(obj->klass())); if (HeapShared::is_writing_streaming_mode()) { AOTStreamedHeapWriter::add_source_obj(obj); @@ -667,11 +689,6 @@ public: }; void HeapShared::add_scratch_resolved_references(ConstantPool* src, objArrayOop dest) { - if (CDSConfig::is_dumping_preimage_static_archive() && scratch_resolved_references(src) != nullptr) { - // We are in AOT training run. The class has been redefined and we are giving it a new resolved_reference. - // Ignore it, as this class will be excluded from the AOT config. - return; - } if (SystemDictionaryShared::is_builtin_loader(src->pool_holder()->class_loader_data())) { _scratch_objects_table->set_oop(src, dest); } @@ -681,10 +698,17 @@ objArrayOop HeapShared::scratch_resolved_references(ConstantPool* src) { return (objArrayOop)_scratch_objects_table->get_oop(src); } +void HeapShared::remove_scratch_resolved_references(ConstantPool* src) { + if (CDSConfig::is_dumping_heap()) { + _scratch_objects_table->remove_oop(src); + } +} + void HeapShared::init_dumping() { _scratch_objects_table = new (mtClass)MetaspaceObjToOopHandleTable(); _pending_roots = new GrowableArrayCHeap(500); _pending_roots->append(nullptr); // root index 0 represents a null oop + DEBUG_ONLY(_dumptime_classes_with_cached_oops = new (mtClassShared)ArchivableKlassTable()); } void HeapShared::init_scratch_objects_for_basic_type_mirrors(TRAPS) { @@ -966,6 +990,8 @@ void HeapShared::write_heap(AOTMappedHeapInfo* mapped_heap_info, AOTStreamedHeap ArchiveBuilder::OtherROAllocMark mark; write_subgraph_info_table(); + DEBUG_ONLY(_runtime_classes_with_cached_oops = _dumptime_classes_with_cached_oops->write_ordered_array()); + delete _pending_roots; _pending_roots = nullptr; @@ -1277,6 +1303,7 @@ void HeapShared::serialize_tables(SerializeClosure* soc) { _run_time_subgraph_info_table.serialize_header(soc); soc->do_ptr(&_run_time_special_subgraph); + DEBUG_ONLY(soc->do_ptr(&_runtime_classes_with_cached_oops)); } static void verify_the_heap(Klass* k, const char* which) { diff --git a/src/hotspot/share/cds/heapShared.hpp b/src/hotspot/share/cds/heapShared.hpp index c3ad1f666b1..10ea35ab56e 100644 --- a/src/hotspot/share/cds/heapShared.hpp +++ b/src/hotspot/share/cds/heapShared.hpp @@ -451,6 +451,7 @@ private: static void write_heap(AOTMappedHeapInfo* mapped_heap_info, AOTStreamedHeapInfo* streamed_heap_info) NOT_CDS_JAVA_HEAP_RETURN; static objArrayOop scratch_resolved_references(ConstantPool* src); static void add_scratch_resolved_references(ConstantPool* src, objArrayOop dest) NOT_CDS_JAVA_HEAP_RETURN; + static void remove_scratch_resolved_references(ConstantPool* src) NOT_CDS_JAVA_HEAP_RETURN; static void init_dumping() NOT_CDS_JAVA_HEAP_RETURN; static void init_scratch_objects_for_basic_type_mirrors(TRAPS) NOT_CDS_JAVA_HEAP_RETURN; static void init_box_classes(TRAPS) NOT_CDS_JAVA_HEAP_RETURN; diff --git a/src/hotspot/share/cds/lambdaProxyClassDictionary.hpp b/src/hotspot/share/cds/lambdaProxyClassDictionary.hpp index dfb75532917..b20e998bba6 100644 --- a/src/hotspot/share/cds/lambdaProxyClassDictionary.hpp +++ b/src/hotspot/share/cds/lambdaProxyClassDictionary.hpp @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_CDS_LAMBDAPROXYCLASSINFO_HPP -#define SHARE_CDS_LAMBDAPROXYCLASSINFO_HPP +#ifndef SHARE_CDS_LAMBDAPROXYCLASSDICTIONARY_HPP +#define SHARE_CDS_LAMBDAPROXYCLASSDICTIONARY_HPP #include "cds/aotCompressedPointers.hpp" #include "cds/aotMetaspace.hpp" @@ -331,4 +331,4 @@ public: static void print_statistics(outputStream* st, bool is_static_archive); }; -#endif // SHARE_CDS_LAMBDAPROXYCLASSINFO_HPP +#endif // SHARE_CDS_LAMBDAPROXYCLASSDICTIONARY_HPP diff --git a/src/hotspot/share/ci/ciMethodData.cpp b/src/hotspot/share/ci/ciMethodData.cpp index 533e8659968..5e623e2b965 100644 --- a/src/hotspot/share/ci/ciMethodData.cpp +++ b/src/hotspot/share/ci/ciMethodData.cpp @@ -537,8 +537,8 @@ void ciMethodData::clear_escape_info() { if (mdo != nullptr) { mdo->clear_escape_info(); ArgInfoData *aid = arg_info(); - int arg_count = (aid == nullptr) ? 0 : aid->number_of_args(); - for (int i = 0; i < arg_count; i++) { + int arg_size = (aid == nullptr) ? 0 : aid->size_of_args(); + for (int i = 0; i < arg_size; i++) { set_arg_modified(i, 0); } } @@ -554,8 +554,8 @@ void ciMethodData::update_escape_info() { mdo->set_arg_local(_arg_local); mdo->set_arg_stack(_arg_stack); mdo->set_arg_returned(_arg_returned); - int arg_count = mdo->method()->size_of_parameters(); - for (int i = 0; i < arg_count; i++) { + int arg_size = mdo->method()->size_of_parameters(); + for (int i = 0; i < arg_size; i++) { mdo->set_arg_modified(i, arg_modified(i)); } } @@ -652,7 +652,7 @@ void ciMethodData::set_arg_modified(int arg, uint val) { ArgInfoData *aid = arg_info(); if (aid == nullptr) return; - assert(arg >= 0 && arg < aid->number_of_args(), "valid argument number"); + assert(arg >= 0 && arg < aid->size_of_args(), "valid argument number"); aid->set_arg_modified(arg, val); } @@ -672,7 +672,7 @@ uint ciMethodData::arg_modified(int arg) const { ArgInfoData *aid = arg_info(); if (aid == nullptr) return 0; - assert(arg >= 0 && arg < aid->number_of_args(), "valid argument number"); + assert(arg >= 0 && arg < aid->size_of_args(), "valid argument number"); return aid->arg_modified(arg); } diff --git a/src/hotspot/share/ci/ciReplay.cpp b/src/hotspot/share/ci/ciReplay.cpp index 6266c024260..35522e75877 100644 --- a/src/hotspot/share/ci/ciReplay.cpp +++ b/src/hotspot/share/ci/ciReplay.cpp @@ -600,7 +600,7 @@ class CompileReplay : public StackObj { _nesting.check(); // Check if a reallocation in the resource arena is safe int new_length = _buffer_length * 2; // Next call will throw error in case of OOM. - _buffer = REALLOC_RESOURCE_ARRAY(char, _buffer, _buffer_length, new_length); + _buffer = REALLOC_RESOURCE_ARRAY(_buffer, _buffer_length, new_length); _buffer_length = new_length; } if (c == '\n') { diff --git a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp index c1f00cbe536..d5ee16fec32 100644 --- a/src/hotspot/share/classfile/classFileParser.cpp +++ b/src/hotspot/share/classfile/classFileParser.cpp @@ -34,6 +34,7 @@ #include "classfile/packageEntry.hpp" #include "classfile/symbolTable.hpp" #include "classfile/systemDictionary.hpp" +#include "classfile/systemDictionaryShared.hpp" #include "classfile/verificationType.hpp" #include "classfile/verifier.hpp" #include "classfile/vmClasses.hpp" @@ -86,9 +87,6 @@ #include "utilities/macros.hpp" #include "utilities/ostream.hpp" #include "utilities/utf8.hpp" -#if INCLUDE_CDS -#include "classfile/systemDictionaryShared.hpp" -#endif // We generally try to create the oops directly when parsing, rather than // allocating temporary data structures and copying the bytes twice. A @@ -194,7 +192,7 @@ void ClassFileParser::parse_constant_pool_entries(const ClassFileStream* const s // so we don't need bounds-check for reading tag. const u1 tag = cfs->get_u1_fast(); switch (tag) { - case JVM_CONSTANT_Class : { + case JVM_CONSTANT_Class: { cfs->guarantee_more(3, CHECK); // name_index, tag/access_flags const u2 name_index = cfs->get_u2_fast(); cp->klass_index_at_put(index, name_index); @@ -2365,8 +2363,8 @@ Method* ClassFileParser::parse_method(const ClassFileStream* const cfs, } if (lvt_cnt == max_lvt_cnt) { max_lvt_cnt <<= 1; - localvariable_table_length = REALLOC_RESOURCE_ARRAY(u2, localvariable_table_length, lvt_cnt, max_lvt_cnt); - localvariable_table_start = REALLOC_RESOURCE_ARRAY(const unsafe_u2*, localvariable_table_start, lvt_cnt, max_lvt_cnt); + localvariable_table_length = REALLOC_RESOURCE_ARRAY(localvariable_table_length, lvt_cnt, max_lvt_cnt); + localvariable_table_start = REALLOC_RESOURCE_ARRAY(localvariable_table_start, lvt_cnt, max_lvt_cnt); } localvariable_table_start[lvt_cnt] = parse_localvariable_table(cfs, @@ -2395,8 +2393,8 @@ Method* ClassFileParser::parse_method(const ClassFileStream* const cfs, // Parse local variable type table if (lvtt_cnt == max_lvtt_cnt) { max_lvtt_cnt <<= 1; - localvariable_type_table_length = REALLOC_RESOURCE_ARRAY(u2, localvariable_type_table_length, lvtt_cnt, max_lvtt_cnt); - localvariable_type_table_start = REALLOC_RESOURCE_ARRAY(const unsafe_u2*, localvariable_type_table_start, lvtt_cnt, max_lvtt_cnt); + localvariable_type_table_length = REALLOC_RESOURCE_ARRAY(localvariable_type_table_length, lvtt_cnt, max_lvtt_cnt); + localvariable_type_table_start = REALLOC_RESOURCE_ARRAY(localvariable_type_table_start, lvtt_cnt, max_lvtt_cnt); } localvariable_type_table_start[lvtt_cnt] = parse_localvariable_table(cfs, @@ -4403,14 +4401,14 @@ void ClassFileParser::verify_legal_field_modifiers(jint flags, TRAPS) const { if (!_need_verify) { return; } - const bool is_public = (flags & JVM_ACC_PUBLIC) != 0; - const bool is_protected = (flags & JVM_ACC_PROTECTED) != 0; - const bool is_private = (flags & JVM_ACC_PRIVATE) != 0; - const bool is_static = (flags & JVM_ACC_STATIC) != 0; - const bool is_final = (flags & JVM_ACC_FINAL) != 0; - const bool is_volatile = (flags & JVM_ACC_VOLATILE) != 0; - const bool is_transient = (flags & JVM_ACC_TRANSIENT) != 0; - const bool is_enum = (flags & JVM_ACC_ENUM) != 0; + const bool is_public = (flags & JVM_ACC_PUBLIC) != 0; + const bool is_protected = (flags & JVM_ACC_PROTECTED) != 0; + const bool is_private = (flags & JVM_ACC_PRIVATE) != 0; + const bool is_static = (flags & JVM_ACC_STATIC) != 0; + const bool is_final = (flags & JVM_ACC_FINAL) != 0; + const bool is_volatile = (flags & JVM_ACC_VOLATILE) != 0; + const bool is_transient = (flags & JVM_ACC_TRANSIENT) != 0; + const bool is_enum = (flags & JVM_ACC_ENUM) != 0; const bool major_gte_1_5 = _major_version >= JAVA_1_5_VERSION; bool is_illegal = false; @@ -5256,6 +5254,9 @@ void ClassFileParser::fill_instance_klass(InstanceKlass* ik, if (!is_internal()) { ik->print_class_load_logging(_loader_data, module_entry, _stream); + if (CDSConfig::is_dumping_archive()) { + SystemDictionaryShared::check_code_source(ik, _stream); + } if (ik->minor_version() == JAVA_PREVIEW_MINOR_VERSION && ik->major_version() == JVM_CLASSFILE_MAJOR_VERSION && diff --git a/src/hotspot/share/classfile/classLoader.cpp b/src/hotspot/share/classfile/classLoader.cpp index eced83577cb..c19a1770aef 100644 --- a/src/hotspot/share/classfile/classLoader.cpp +++ b/src/hotspot/share/classfile/classLoader.cpp @@ -251,7 +251,7 @@ const char* ClassPathEntry::copy_path(const char* path) { } ClassPathDirEntry::~ClassPathDirEntry() { - FREE_C_HEAP_ARRAY(char, _dir); + FREE_C_HEAP_ARRAY(_dir); } ClassFileStream* ClassPathDirEntry::open_stream(JavaThread* current, const char* name) { @@ -280,7 +280,7 @@ ClassFileStream* ClassPathDirEntry::open_stream(JavaThread* current, const char* #ifdef ASSERT // Freeing path is a no-op here as buffer prevents it from being reclaimed. But we keep it for // debug builds so that we guard against use-after-free bugs. - FREE_RESOURCE_ARRAY_IN_THREAD(current, char, path, path_len); + FREE_RESOURCE_ARRAY_IN_THREAD(current, path, path_len); #endif // We don't verify the length of the classfile stream fits in an int, but this is the // bootloader so we have control of this. @@ -291,7 +291,7 @@ ClassFileStream* ClassPathDirEntry::open_stream(JavaThread* current, const char* } } } - FREE_RESOURCE_ARRAY_IN_THREAD(current, char, path, path_len); + FREE_RESOURCE_ARRAY_IN_THREAD(current, path, path_len); return nullptr; } @@ -302,7 +302,7 @@ ClassPathZipEntry::ClassPathZipEntry(jzfile* zip, const char* zip_name) : ClassP ClassPathZipEntry::~ClassPathZipEntry() { ZipLibrary::close(_zip); - FREE_C_HEAP_ARRAY(char, _zip_name); + FREE_C_HEAP_ARRAY(_zip_name); } bool ClassPathZipEntry::has_entry(JavaThread* current, const char* name) { @@ -1438,7 +1438,7 @@ bool ClassLoader::is_module_observable(const char* module_name) { struct stat st; const char *path = get_exploded_module_path(module_name, true); bool res = os::stat(path, &st) == 0; - FREE_C_HEAP_ARRAY(char, path); + FREE_C_HEAP_ARRAY(path); return res; } jlong size; diff --git a/src/hotspot/share/classfile/classLoaderData.cpp b/src/hotspot/share/classfile/classLoaderData.cpp index dfc3b74db96..d1ea9c09d4c 100644 --- a/src/hotspot/share/classfile/classLoaderData.cpp +++ b/src/hotspot/share/classfile/classLoaderData.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, 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 @@ -46,6 +46,7 @@ // The bootstrap loader (represented by null) also has a ClassLoaderData, // the singleton class the_null_class_loader_data(). +#include "cds/heapShared.hpp" #include "classfile/classLoaderData.inline.hpp" #include "classfile/classLoaderDataGraph.inline.hpp" #include "classfile/dictionary.hpp" @@ -899,6 +900,7 @@ void ClassLoaderData::free_deallocate_list() { if (m->is_method()) { MetadataFactory::free_metadata(this, (Method*)m); } else if (m->is_constantPool()) { + HeapShared::remove_scratch_resolved_references((ConstantPool*)m); MetadataFactory::free_metadata(this, (ConstantPool*)m); } else if (m->is_klass()) { MetadataFactory::free_metadata(this, (InstanceKlass*)m); diff --git a/src/hotspot/share/classfile/compactHashtable.cpp b/src/hotspot/share/classfile/compactHashtable.cpp index de67971c403..f06f1986d8b 100644 --- a/src/hotspot/share/classfile/compactHashtable.cpp +++ b/src/hotspot/share/classfile/compactHashtable.cpp @@ -69,7 +69,7 @@ CompactHashtableWriter::~CompactHashtableWriter() { delete bucket; } - FREE_C_HEAP_ARRAY(GrowableArray*, _buckets); + FREE_C_HEAP_ARRAY(_buckets); } // Add an entry to the temporary hash table diff --git a/src/hotspot/share/classfile/compactHashtable.hpp b/src/hotspot/share/classfile/compactHashtable.hpp index 81f2951289d..1711c5f8cd3 100644 --- a/src/hotspot/share/classfile/compactHashtable.hpp +++ b/src/hotspot/share/classfile/compactHashtable.hpp @@ -307,14 +307,9 @@ public: template inline void iterate(ITER* iter) const { iterate([&](V v) { iter->do_value(v); }); } - template - inline void iterate(const Function& function) const { // lambda enabled API - iterate(const_cast(function)); - } - // Iterate through the values in the table, stopping when the lambda returns false. template - inline void iterate(Function& function) const { // lambda enabled API + inline void iterate(Function function) const { // lambda enabled API for (u4 i = 0; i < _bucket_count; i++) { u4 bucket_info = _buckets[i]; u4 bucket_offset = BUCKET_OFFSET(bucket_info); diff --git a/src/hotspot/share/classfile/fieldLayoutBuilder.cpp b/src/hotspot/share/classfile/fieldLayoutBuilder.cpp index a87e12edc96..adf4e1e63fa 100644 --- a/src/hotspot/share/classfile/fieldLayoutBuilder.cpp +++ b/src/hotspot/share/classfile/fieldLayoutBuilder.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -37,7 +37,7 @@ LayoutRawBlock::LayoutRawBlock(Kind kind, int size) : _next_block(nullptr), _prev_block(nullptr), - _kind(kind), + _block_kind(kind), _offset(-1), _alignment(1), _size(size), @@ -52,7 +52,7 @@ LayoutRawBlock::LayoutRawBlock(Kind kind, int size) : LayoutRawBlock::LayoutRawBlock(int index, Kind kind, int size, int alignment, bool is_reference) : _next_block(nullptr), _prev_block(nullptr), - _kind(kind), + _block_kind(kind), _offset(-1), _alignment(alignment), _size(size), @@ -148,8 +148,8 @@ void FieldLayout::initialize_instance_layout(const InstanceKlass* super_klass, b LayoutRawBlock* FieldLayout::first_field_block() { LayoutRawBlock* block = _start; - while (block->kind() != LayoutRawBlock::INHERITED && block->kind() != LayoutRawBlock::REGULAR - && block->kind() != LayoutRawBlock::FLATTENED && block->kind() != LayoutRawBlock::PADDING) { + while (block->block_kind() != LayoutRawBlock::INHERITED && block->block_kind() != LayoutRawBlock::REGULAR + && block->block_kind() != LayoutRawBlock::FLATTENED && block->block_kind() != LayoutRawBlock::PADDING) { block = block->next_block(); } return block; @@ -190,7 +190,7 @@ void FieldLayout::add(GrowableArray* list, LayoutRawBlock* star assert(cursor != nullptr, "Sanity check"); last_search_success = true; while (cursor != start) { - if (cursor->kind() == LayoutRawBlock::EMPTY && cursor->fit(b->size(), b->alignment())) { + if (cursor->block_kind() == LayoutRawBlock::EMPTY && cursor->fit(b->size(), b->alignment())) { if (candidate == nullptr || cursor->size() < candidate->size()) { candidate = cursor; } @@ -202,7 +202,7 @@ void FieldLayout::add(GrowableArray* list, LayoutRawBlock* star last_search_success = false; } assert(candidate != nullptr, "Candidate must not be null"); - assert(candidate->kind() == LayoutRawBlock::EMPTY, "Candidate must be an empty block"); + assert(candidate->block_kind() == LayoutRawBlock::EMPTY, "Candidate must be an empty block"); assert(candidate->fit(b->size(), b->alignment()), "Candidate must be able to store the block"); } @@ -221,7 +221,7 @@ void FieldLayout::add_field_at_offset(LayoutRawBlock* block, int offset, LayoutR while (slot != nullptr) { if ((slot->offset() <= block->offset() && (slot->offset() + slot->size()) > block->offset()) || slot == _last){ - assert(slot->kind() == LayoutRawBlock::EMPTY, "Matching slot must be an empty slot"); + assert(slot->block_kind() == LayoutRawBlock::EMPTY, "Matching slot must be an empty slot"); assert(slot->size() >= block->offset() + block->size() ,"Matching slot must be big enough"); if (slot->offset() < block->offset()) { int adjustment = block->offset() - slot->offset(); @@ -261,7 +261,7 @@ void FieldLayout::add_contiguously(GrowableArray* list, LayoutR } else { LayoutRawBlock* first = list->at(0); candidate = last_block()->prev_block(); - while (candidate->kind() != LayoutRawBlock::EMPTY || !candidate->fit(size, first->alignment())) { + while (candidate->block_kind() != LayoutRawBlock::EMPTY || !candidate->fit(size, first->alignment())) { if (candidate == start) { candidate = last_block(); break; @@ -269,7 +269,7 @@ void FieldLayout::add_contiguously(GrowableArray* list, LayoutR candidate = candidate->prev_block(); } assert(candidate != nullptr, "Candidate must not be null"); - assert(candidate->kind() == LayoutRawBlock::EMPTY, "Candidate must be an empty block"); + assert(candidate->block_kind() == LayoutRawBlock::EMPTY, "Candidate must be an empty block"); assert(candidate->fit(size, first->alignment()), "Candidate must be able to store the whole contiguous block"); } @@ -281,7 +281,7 @@ void FieldLayout::add_contiguously(GrowableArray* list, LayoutR } LayoutRawBlock* FieldLayout::insert_field_block(LayoutRawBlock* slot, LayoutRawBlock* block) { - assert(slot->kind() == LayoutRawBlock::EMPTY, "Blocks can only be inserted in empty blocks"); + assert(slot->block_kind() == LayoutRawBlock::EMPTY, "Blocks can only be inserted in empty blocks"); if (slot->offset() % block->alignment() != 0) { int adjustment = block->alignment() - (slot->offset() % block->alignment()); LayoutRawBlock* adj = new LayoutRawBlock(LayoutRawBlock::EMPTY, adjustment); @@ -362,7 +362,7 @@ void FieldLayout::fill_holes(const InstanceKlass* super_klass) { b = b->next_block(); } assert(b->next_block() == nullptr, "Invariant at this point"); - assert(b->kind() != LayoutRawBlock::EMPTY, "Sanity check"); + assert(b->block_kind() != LayoutRawBlock::EMPTY, "Sanity check"); // If the super class has @Contended annotation, a padding block is // inserted at the end to ensure that fields from the subclasses won't share @@ -384,7 +384,7 @@ void FieldLayout::fill_holes(const InstanceKlass* super_klass) { } LayoutRawBlock* FieldLayout::insert(LayoutRawBlock* slot, LayoutRawBlock* block) { - assert(slot->kind() == LayoutRawBlock::EMPTY, "Blocks can only be inserted in empty blocks"); + assert(slot->block_kind() == LayoutRawBlock::EMPTY, "Blocks can only be inserted in empty blocks"); assert(slot->offset() % block->alignment() == 0, "Incompatible alignment"); block->set_offset(slot->offset()); slot->set_offset(slot->offset() + block->size()); @@ -425,7 +425,7 @@ void FieldLayout::print(outputStream* output, bool is_static, const InstanceKlas ResourceMark rm; LayoutRawBlock* b = _blocks; while(b != _last) { - switch(b->kind()) { + switch(b->block_kind()) { case LayoutRawBlock::REGULAR: { FieldInfo* fi = _field_info->adr_at(b->field_index()); output->print_cr(" @%d \"%s\" %s %d/%d %s", @@ -596,11 +596,13 @@ void FieldLayoutBuilder::regular_field_sorting() { } } -void FieldLayoutBuilder::insert_contended_padding(LayoutRawBlock* slot) { +LayoutRawBlock* FieldLayoutBuilder::insert_contended_padding(LayoutRawBlock* slot) { + LayoutRawBlock* padding = nullptr; if (ContendedPaddingWidth > 0) { - LayoutRawBlock* padding = new LayoutRawBlock(LayoutRawBlock::PADDING, ContendedPaddingWidth); + padding = new LayoutRawBlock(LayoutRawBlock::PADDING, ContendedPaddingWidth); _layout->insert(slot, padding); } + return padding; } // Computation of regular classes layout is an evolution of the previous default layout @@ -620,10 +622,14 @@ void FieldLayoutBuilder::compute_regular_layout() { regular_field_sorting(); if (_is_contended) { - _layout->set_start(_layout->last_block()); // insertion is currently easy because the current strategy doesn't try to fill holes // in super classes layouts => the _start block is by consequence the _last_block - insert_contended_padding(_layout->start()); + _layout->set_start(_layout->last_block()); + LayoutRawBlock* padding = insert_contended_padding(_layout->start()); + if (padding != nullptr) { + // Setting the padding block as start ensures we do not insert past it. + _layout->set_start(padding); + } need_tail_padding = true; } @@ -639,7 +645,13 @@ void FieldLayoutBuilder::compute_regular_layout() { for (int i = 0; i < _contended_groups.length(); i++) { FieldGroup* cg = _contended_groups.at(i); LayoutRawBlock* start = _layout->last_block(); - insert_contended_padding(start); + LayoutRawBlock* padding = insert_contended_padding(start); + + // Do not insert fields past the padding block. + if (padding != nullptr) { + start = padding; + } + _layout->add(cg->primitive_fields(), start); _layout->add(cg->oop_fields(), start); need_tail_padding = true; diff --git a/src/hotspot/share/classfile/fieldLayoutBuilder.hpp b/src/hotspot/share/classfile/fieldLayoutBuilder.hpp index 82bbaefc623..a45131ec9a3 100644 --- a/src/hotspot/share/classfile/fieldLayoutBuilder.hpp +++ b/src/hotspot/share/classfile/fieldLayoutBuilder.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -65,7 +65,7 @@ class LayoutRawBlock : public ResourceObj { private: LayoutRawBlock* _next_block; LayoutRawBlock* _prev_block; - Kind _kind; + Kind _block_kind; int _offset; int _alignment; int _size; @@ -79,7 +79,7 @@ class LayoutRawBlock : public ResourceObj { void set_next_block(LayoutRawBlock* next) { _next_block = next; } LayoutRawBlock* prev_block() const { return _prev_block; } void set_prev_block(LayoutRawBlock* prev) { _prev_block = prev; } - Kind kind() const { return _kind; } + Kind block_kind() const { return _block_kind; } int offset() const { assert(_offset >= 0, "Must be initialized"); return _offset; @@ -173,7 +173,7 @@ class FieldLayout : public ResourceObj { LayoutRawBlock* first_empty_block() { LayoutRawBlock* block = _start; - while (block->kind() != LayoutRawBlock::EMPTY) { + while (block->block_kind() != LayoutRawBlock::EMPTY) { block = block->next_block(); } return block; @@ -250,7 +250,7 @@ class FieldLayoutBuilder : public ResourceObj { void build_layout(); void compute_regular_layout(); - void insert_contended_padding(LayoutRawBlock* slot); + LayoutRawBlock* insert_contended_padding(LayoutRawBlock* slot); private: void prologue(); diff --git a/src/hotspot/share/classfile/moduleEntry.cpp b/src/hotspot/share/classfile/moduleEntry.cpp index b5b8aa4ef55..c7fadeaea9b 100644 --- a/src/hotspot/share/classfile/moduleEntry.cpp +++ b/src/hotspot/share/classfile/moduleEntry.cpp @@ -23,7 +23,6 @@ */ #include "cds/aotClassLocation.hpp" -#include "cds/aotGrowableArray.inline.hpp" #include "cds/archiveBuilder.hpp" #include "cds/archiveUtils.hpp" #include "cds/cdsConfig.hpp" @@ -168,7 +167,7 @@ void ModuleEntry::add_read(ModuleEntry* m) { } else { if (reads() == nullptr) { // Lazily create a module's reads list - AOTGrowableArray* new_reads = new (mtModule) AOTGrowableArray(MODULE_READS_SIZE, mtModule); + GrowableArray* new_reads = new (mtModule) GrowableArray(MODULE_READS_SIZE, mtModule); set_reads(new_reads); } diff --git a/src/hotspot/share/classfile/moduleEntry.hpp b/src/hotspot/share/classfile/moduleEntry.hpp index 1a0251a2c2a..10dec73e9fa 100644 --- a/src/hotspot/share/classfile/moduleEntry.hpp +++ b/src/hotspot/share/classfile/moduleEntry.hpp @@ -25,7 +25,6 @@ #ifndef SHARE_CLASSFILE_MODULEENTRY_HPP #define SHARE_CLASSFILE_MODULEENTRY_HPP -#include "cds/aotGrowableArray.hpp" #include "jni.h" #include "memory/metaspaceClosureType.hpp" #include "oops/oopHandle.hpp" @@ -70,7 +69,7 @@ private: // for shared classes from this module Symbol* _name; // name of this module ClassLoaderData* _loader_data; - AOTGrowableArray* _reads; // list of modules that are readable by this module + GrowableArray* _reads; // list of modules that are readable by this module Symbol* _version; // module version number Symbol* _location; // module location @@ -118,10 +117,10 @@ public: bool can_read(ModuleEntry* m) const; bool has_reads_list() const; - AOTGrowableArray* reads() const { + GrowableArray* reads() const { return _reads; } - void set_reads(AOTGrowableArray* r) { + void set_reads(GrowableArray* r) { _reads = r; } void pack_reads() { diff --git a/src/hotspot/share/classfile/packageEntry.cpp b/src/hotspot/share/classfile/packageEntry.cpp index 3e61f2e3a3e..3eb50fcb5a7 100644 --- a/src/hotspot/share/classfile/packageEntry.cpp +++ b/src/hotspot/share/classfile/packageEntry.cpp @@ -22,7 +22,6 @@ * */ -#include "cds/aotGrowableArray.inline.hpp" #include "cds/aotMetaspace.hpp" #include "cds/archiveBuilder.hpp" #include "cds/archiveUtils.hpp" @@ -83,7 +82,7 @@ void PackageEntry::add_qexport(ModuleEntry* m) { if (!has_qual_exports_list()) { // Lazily create a package's qualified exports list. // Initial size is small, do not anticipate export lists to be large. - _qualified_exports = new (mtModule) AOTGrowableArray(QUAL_EXP_SIZE, mtModule); + _qualified_exports = new (mtModule) GrowableArray(QUAL_EXP_SIZE, mtModule); } // Determine, based on this newly established export to module m, diff --git a/src/hotspot/share/classfile/packageEntry.hpp b/src/hotspot/share/classfile/packageEntry.hpp index 7b174a92287..e064e53b263 100644 --- a/src/hotspot/share/classfile/packageEntry.hpp +++ b/src/hotspot/share/classfile/packageEntry.hpp @@ -25,7 +25,6 @@ #ifndef SHARE_CLASSFILE_PACKAGEENTRY_HPP #define SHARE_CLASSFILE_PACKAGEENTRY_HPP -#include "cds/aotGrowableArray.hpp" #include "classfile/moduleEntry.hpp" #include "memory/metaspaceClosureType.hpp" #include "oops/symbol.hpp" @@ -116,7 +115,7 @@ private: bool _must_walk_exports; // Contains list of modules this package is qualifiedly exported to. Access // to this list is protected by the Module_lock. - AOTGrowableArray* _qualified_exports; + GrowableArray* _qualified_exports; JFR_ONLY(DEFINE_TRACE_ID_FIELD;) // Initial size of a package entry's list of qualified exports. diff --git a/src/hotspot/share/classfile/resolutionErrors.cpp b/src/hotspot/share/classfile/resolutionErrors.cpp index c41d5d2f052..958d4812cbb 100644 --- a/src/hotspot/share/classfile/resolutionErrors.cpp +++ b/src/hotspot/share/classfile/resolutionErrors.cpp @@ -114,15 +114,15 @@ ResolutionErrorEntry::~ResolutionErrorEntry() { Symbol::maybe_decrement_refcount(_cause); if (_message != nullptr) { - FREE_C_HEAP_ARRAY(char, _message); + FREE_C_HEAP_ARRAY(_message); } if (_cause_msg != nullptr) { - FREE_C_HEAP_ARRAY(char, _cause_msg); + FREE_C_HEAP_ARRAY(_cause_msg); } if (nest_host_error() != nullptr) { - FREE_C_HEAP_ARRAY(char, nest_host_error()); + FREE_C_HEAP_ARRAY(nest_host_error()); } } diff --git a/src/hotspot/share/classfile/stackMapTableFormat.hpp b/src/hotspot/share/classfile/stackMapTableFormat.hpp index 2b89c53278a..4906f4b9d80 100644 --- a/src/hotspot/share/classfile/stackMapTableFormat.hpp +++ b/src/hotspot/share/classfile/stackMapTableFormat.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2026, 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 @@ -221,9 +221,11 @@ class stack_map_frame { class same_frame : public stack_map_frame { private: static int frame_type_to_offset_delta(u1 frame_type) { - return frame_type + 1; } + return frame_type + 1; + } static u1 offset_delta_to_frame_type(int offset_delta) { - return checked_cast(offset_delta - 1); } + return checked_cast(offset_delta - 1); + } public: @@ -327,9 +329,11 @@ class same_locals_1_stack_item_frame : public stack_map_frame { address type_addr() const { return frame_type_addr() + sizeof(u1); } static int frame_type_to_offset_delta(u1 frame_type) { - return frame_type - 63; } + return frame_type - 63; + } static u1 offset_delta_to_frame_type(int offset_delta) { - return (u1)(offset_delta + 63); } + return (u1)(offset_delta + 63); + } public: static bool is_frame_type(u1 tag) { @@ -657,9 +661,11 @@ class full_frame : public stack_map_frame { address num_locals_addr() const { return offset_delta_addr() + sizeof(u2); } address locals_addr() const { return num_locals_addr() + sizeof(u2); } address stack_slots_addr(address end_of_locals) const { - return end_of_locals; } + return end_of_locals; + } address stack_addr(address end_of_locals) const { - return stack_slots_addr(end_of_locals) + sizeof(u2); } + return stack_slots_addr(end_of_locals) + sizeof(u2); + } enum { _frame_id = 255 }; @@ -930,11 +936,14 @@ class stack_map_table { class stack_map_table_attribute { private: address name_index_addr() const { - return (address)this; } + return (address)this; + } address attribute_length_addr() const { - return name_index_addr() + sizeof(u2); } + return name_index_addr() + sizeof(u2); + } address stack_map_table_addr() const { - return attribute_length_addr() + sizeof(u4); } + return attribute_length_addr() + sizeof(u4); + } NONCOPYABLE(stack_map_table_attribute); protected: @@ -948,9 +957,11 @@ class stack_map_table_attribute { } u2 name_index() const { - return Bytes::get_Java_u2(name_index_addr()); } + return Bytes::get_Java_u2(name_index_addr()); + } u4 attribute_length() const { - return Bytes::get_Java_u4(attribute_length_addr()); } + return Bytes::get_Java_u4(attribute_length_addr()); + } stack_map_table* table() const { return stack_map_table::at(stack_map_table_addr()); } diff --git a/src/hotspot/share/classfile/systemDictionary.cpp b/src/hotspot/share/classfile/systemDictionary.cpp index 0b47c749df8..8483551fd4f 100644 --- a/src/hotspot/share/classfile/systemDictionary.cpp +++ b/src/hotspot/share/classfile/systemDictionary.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -416,7 +416,7 @@ static inline void log_circularity_error(Symbol* name, PlaceholderEntry* probe) // // resolve_with_circularity_detection adds a DETECT_CIRCULARITY placeholder to the placeholder table before calling // resolve_instance_class_or_null. ClassCircularityError is detected when a DETECT_CIRCULARITY or LOAD_INSTANCE -// placeholder for the same thread, class, classloader is found. +// placeholder for the same thread, class, and classloader is found. // This can be seen with logging option: -Xlog:class+load+placeholders=debug. // InstanceKlass* SystemDictionary::resolve_with_circularity_detection(Symbol* class_name, diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp index 5947050e31a..fd30fc6766f 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.cpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp @@ -89,11 +89,9 @@ DEBUG_ONLY(bool SystemDictionaryShared::_class_loading_may_happen = true;) #ifdef ASSERT static void check_klass_after_loading(const Klass* k) { -#ifdef _LP64 - if (k != nullptr && UseCompressedClassPointers) { + if (k != nullptr) { CompressedKlassPointers::check_encodable(k); } -#endif } #endif @@ -204,6 +202,20 @@ DumpTimeClassInfo* SystemDictionaryShared::get_info_locked(InstanceKlass* k) { return info; } +void SystemDictionaryShared::check_code_source(InstanceKlass* ik, const ClassFileStream* cfs) { + if (CDSConfig::is_dumping_preimage_static_archive() && !is_builtin_loader(ik->class_loader_data())) { + if (cfs == nullptr || cfs->source() == nullptr || strncmp(cfs->source(), "file:", 5) != 0) { + // AOT cache filtering: + // For non-built-in loaders, cache only the classes that have a file: code source, so + // we can avoid caching dynamically generated classes that are likely to change from + // run to run. This is similar to the filtering in ClassListWriter::write_to_stream() + // for the classic CDS static archive. + SystemDictionaryShared::log_exclusion(ik, "Not loaded from \"file:\" code source"); + SystemDictionaryShared::set_excluded(ik); + } + } +} + bool SystemDictionaryShared::should_be_excluded_impl(InstanceKlass* k, DumpTimeClassInfo* info) { assert_lock_strong(DumpTimeTable_lock); diff --git a/src/hotspot/share/classfile/systemDictionaryShared.hpp b/src/hotspot/share/classfile/systemDictionaryShared.hpp index 33b245e26fc..c837a386344 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.hpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.hpp @@ -235,6 +235,7 @@ public: static void update_shared_entry(InstanceKlass* klass, int id); static void set_shared_class_misc_info(InstanceKlass* k, ClassFileStream* cfs); + static void check_code_source(InstanceKlass* ik, const ClassFileStream* cfs) NOT_CDS_RETURN; static InstanceKlass* lookup_from_stream(Symbol* class_name, Handle class_loader, Handle protection_domain, diff --git a/src/hotspot/share/classfile/vmIntrinsics.hpp b/src/hotspot/share/classfile/vmIntrinsics.hpp index e84acd62284..3f85fd16b61 100644 --- a/src/hotspot/share/classfile/vmIntrinsics.hpp +++ b/src/hotspot/share/classfile/vmIntrinsics.hpp @@ -469,6 +469,9 @@ class methodHandle; do_intrinsic(_Reference_clear0, java_lang_ref_Reference, clear0_name, void_method_signature, F_RN) \ do_intrinsic(_PhantomReference_clear0, java_lang_ref_PhantomReference, clear0_name, void_method_signature, F_RN) \ \ + do_intrinsic(_Reference_reachabilityFence, java_lang_ref_Reference, reachabilityFence_name, object_void_signature, F_S) \ + do_name(reachabilityFence_name, "reachabilityFence") \ + \ /* support for com.sun.crypto.provider.AES_Crypt and some of its callers */ \ do_class(com_sun_crypto_provider_aescrypt, "com/sun/crypto/provider/AES_Crypt") \ do_intrinsic(_aescrypt_encryptBlock, com_sun_crypto_provider_aescrypt, encryptBlock_name, byteArray_int_byteArray_int_signature, F_R) \ diff --git a/src/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp index 2ae42bebcfd..33d00b93365 100644 --- a/src/hotspot/share/classfile/vmSymbols.hpp +++ b/src/hotspot/share/classfile/vmSymbols.hpp @@ -702,6 +702,7 @@ class SerializeClosure; template(appendToClassPathForInstrumentation_name, "appendToClassPathForInstrumentation") \ do_alias(appendToClassPathForInstrumentation_signature, string_void_signature) \ template(serializePropertiesToByteArray_name, "serializePropertiesToByteArray") \ + template(serializeSecurityPropertiesToByteArray_name, "serializeSecurityPropertiesToByteArray") \ template(serializeAgentPropertiesToByteArray_name, "serializeAgentPropertiesToByteArray") \ template(encodeThrowable_name, "encodeThrowable") \ template(encodeThrowable_signature, "(Ljava/lang/Throwable;JI)I") \ diff --git a/src/hotspot/share/code/aotCodeCache.cpp b/src/hotspot/share/code/aotCodeCache.cpp index b29cf906736..89b7d16c31e 100644 --- a/src/hotspot/share/code/aotCodeCache.cpp +++ b/src/hotspot/share/code/aotCodeCache.cpp @@ -33,13 +33,18 @@ #include "classfile/javaAssertions.hpp" #include "code/aotCodeCache.hpp" #include "code/codeCache.hpp" +#include "gc/shared/barrierSetAssembler.hpp" +#include "gc/shared/barrierSetNMethod.hpp" #include "gc/shared/cardTableBarrierSet.hpp" #include "gc/shared/gcConfig.hpp" #include "logging/logStream.hpp" #include "memory/memoryReserver.hpp" +#include "prims/jvmtiThreadState.hpp" +#include "prims/upcallLinker.hpp" #include "runtime/deoptimization.hpp" #include "runtime/flags/flagSetting.hpp" #include "runtime/globals_extension.hpp" +#include "runtime/icache.hpp" #include "runtime/java.hpp" #include "runtime/mutexLocker.hpp" #include "runtime/os.inline.hpp" @@ -73,11 +78,23 @@ const char* aot_code_entry_kind_name[] = { #undef DECL_KIND_STRING }; +// Stream to printing AOTCodeCache loading failure. +// Print to error channel when -XX:AOTMode is set to "on" +static LogStream& load_failure_log() { + static LogStream err_stream(LogLevel::Error, LogTagSetMapping::tagset()); + static LogStream dbg_stream(LogLevel::Debug, LogTagSetMapping::tagset()); + if (RequireSharedSpaces) { + return err_stream; + } else { + return dbg_stream; + } +} + static void report_load_failure() { if (AbortVMOnAOTCodeFailure) { vm_exit_during_initialization("Unable to use AOT Code Cache.", nullptr); } - log_info(aot, codecache, init)("Unable to use AOT Code Cache."); + load_failure_log().print_cr("Unable to use AOT Code Cache."); AOTCodeCache::disable_caching(); } @@ -86,7 +103,7 @@ static void report_store_failure() { tty->print_cr("Unable to create AOT Code Cache."); vm_abort(false); } - log_info(aot, codecache, exit)("Unable to create AOT Code Cache."); + log_error(aot, codecache, exit)("Unable to create AOT Code Cache."); AOTCodeCache::disable_caching(); } @@ -156,10 +173,13 @@ static uint32_t encode_id(AOTCodeEntry::Kind kind, int id) { } else if (kind == AOTCodeEntry::C1Blob) { assert(StubInfo::is_c1(static_cast(id)), "not a c1 blob id %d", id); return id; - } else { - // kind must be AOTCodeEntry::C2Blob + } else if (kind == AOTCodeEntry::C2Blob) { assert(StubInfo::is_c2(static_cast(id)), "not a c2 blob id %d", id); return id; + } else { + // kind must be AOTCodeEntry::StubGenBlob + assert(StubInfo::is_stubgen(static_cast(id)), "not a stubgen blob id %d", id); + return id; } } @@ -184,9 +204,6 @@ void AOTCodeCache::initialize() { return; // AOTCache must be specified to dump and use AOT code } - // Disable stubs caching until JDK-8357398 is fixed. - FLAG_SET_ERGO(AOTStubCaching, false); - if (VerifyOops) { // Disable AOT stubs caching when VerifyOops flag is on. // Verify oops code generated a lot of C strings which overflow @@ -284,6 +301,19 @@ bool AOTCodeCache::open_cache(bool is_dumping, bool is_using) { return true; } +// Called after continuations_init() when continuation stub callouts +// have been initialized +void AOTCodeCache::init3() { + if (opened_cache == nullptr) { + return; + } + // initialize external routines for continuations so we can save + // generated continuation blob that references them + AOTCodeAddressTable* table = opened_cache->_table; + assert(table != nullptr, "should be initialized already"); + table->init_extrs2(); +} + void AOTCodeCache::dump() { if (is_on()) { assert(is_on_for_dump(), "should be called only when dumping AOT code"); @@ -342,6 +372,7 @@ AOTCodeCache::AOTCodeCache(bool is_dumping, bool is_using) : log_info (aot, codecache, init)("Loaded %u AOT code entries from AOT Code Cache", _load_header->entries_count()); log_debug(aot, codecache, init)(" Adapters: total=%u", _load_header->adapters_count()); log_debug(aot, codecache, init)(" Shared Blobs: total=%u", _load_header->shared_blobs_count()); + log_debug(aot, codecache, init)(" StubGen Blobs: total=%d", _load_header->stubgen_blobs_count()); log_debug(aot, codecache, init)(" C1 Blobs: total=%u", _load_header->C1_blobs_count()); log_debug(aot, codecache, init)(" C2 Blobs: total=%u", _load_header->C2_blobs_count()); log_debug(aot, codecache, init)(" AOT code cache size: %u bytes", _load_header->cache_size()); @@ -359,58 +390,80 @@ AOTCodeCache::AOTCodeCache(bool is_dumping, bool is_using) : _table = new AOTCodeAddressTable(); } -void AOTCodeCache::init_early_stubs_table() { - AOTCodeAddressTable* table = addr_table(); - if (table != nullptr) { - table->init_early_stubs(); +void AOTCodeCache::add_stub_entries(StubId stub_id, address start, GrowableArray
*entries, int begin_idx) { + EntryId entry_id = StubInfo::entry_base(stub_id); + add_stub_entry(entry_id, start); + // skip past first entry + entry_id = StubInfo::next_in_stub(stub_id, entry_id); + // now check for any more entries + int count = StubInfo::entry_count(stub_id) - 1; + assert(start != nullptr, "invalid start address for stub %s", StubInfo::name(stub_id)); + assert(entries == nullptr || begin_idx + count <= entries->length(), "sanity"); + // write any extra entries + for (int i = 0; i < count; i++) { + assert(entry_id != EntryId::NO_ENTRYID, "not enough entries for stub %s", StubInfo::name(stub_id)); + address a = entries->at(begin_idx + i); + add_stub_entry(entry_id, a); + entry_id = StubInfo::next_in_stub(stub_id, entry_id); + } + assert(entry_id == EntryId::NO_ENTRYID, "too many entries for stub %s", StubInfo::name(stub_id)); +} + +void AOTCodeCache::add_stub_entry(EntryId entry_id, address a) { + if (a != nullptr) { + if (_table != nullptr) { + log_trace(aot, codecache, stubs)("Publishing stub entry %s at address " INTPTR_FORMAT, StubInfo::name(entry_id), p2i(a)); + return _table->add_stub_entry(entry_id, a); + } } } -void AOTCodeCache::init_shared_blobs_table() { +void AOTCodeCache::set_shared_stubs_complete() { AOTCodeAddressTable* table = addr_table(); if (table != nullptr) { - table->init_shared_blobs(); + table->set_shared_stubs_complete(); } } -void AOTCodeCache::init_early_c1_table() { +void AOTCodeCache::set_c1_stubs_complete() { AOTCodeAddressTable* table = addr_table(); if (table != nullptr) { - table->init_early_c1(); + table->set_c1_stubs_complete(); + } +} + +void AOTCodeCache::set_c2_stubs_complete() { + AOTCodeAddressTable* table = addr_table(); + if (table != nullptr) { + table->set_c2_stubs_complete(); + } +} + +void AOTCodeCache::set_stubgen_stubs_complete() { + AOTCodeAddressTable* table = addr_table(); + if (table != nullptr) { + table->set_stubgen_stubs_complete(); } } void AOTCodeCache::Config::record(uint cpu_features_offset) { - _flags = 0; -#ifdef ASSERT - _flags |= debugVM; -#endif - if (UseCompressedOops) { - _flags |= compressedOops; - } - if (UseCompressedClassPointers) { - _flags |= compressedClassPointers; - } - if (UseTLAB) { - _flags |= useTLAB; - } - if (JavaAssertions::systemClassDefault()) { - _flags |= systemClassAssertions; - } - if (JavaAssertions::userClassDefault()) { - _flags |= userClassAssertions; - } - if (EnableContended) { - _flags |= enableContendedPadding; - } - if (RestrictContended) { - _flags |= restrictContendedPadding; - } - _compressedOopShift = CompressedOops::shift(); + +#define AOTCODECACHE_SAVE_VAR(type, name) _saved_ ## name = name; +#define AOTCODECACHE_SAVE_FUN(type, name, fun) _saved_ ## name = fun; + + AOTCODECACHE_CONFIGS_DO(AOTCODECACHE_SAVE_VAR, AOTCODECACHE_SAVE_FUN); + + // Special configs that cannot be checked with macros _compressedOopBase = CompressedOops::base(); - _compressedKlassShift = CompressedKlassPointers::shift(); - _contendedPaddingWidth = ContendedPaddingWidth; - _gc = (uint)Universe::heap()->kind(); + +#if defined(X86) && !defined(ZERO) + _useUnalignedLoadStores = UseUnalignedLoadStores; +#endif + +#if defined(AARCH64) && !defined(ZERO) + _avoidUnalignedAccesses = AvoidUnalignedAccesses; +#endif + _cpu_features_offset = cpu_features_offset; } @@ -441,78 +494,114 @@ bool AOTCodeCache::Config::verify_cpu_features(AOTCodeCache* cache) const { } } } else { - if (log.is_enabled()) { + if (load_failure_log().is_enabled()) { ResourceMark rm; // required for stringStream::as_string() stringStream ss; char* runtime_cpu_features = NEW_RESOURCE_ARRAY(char, VM_Version::cpu_features_size()); VM_Version::store_cpu_features(runtime_cpu_features); VM_Version::get_missing_features_name(cached_cpu_features_buffer, runtime_cpu_features, ss); - log.print_cr("AOT Code Cache disabled: required cpu features are missing: %s", ss.as_string()); + load_failure_log().print_cr("AOT Code Cache disabled: required cpu features are missing: %s", ss.as_string()); } return false; } return true; } -bool AOTCodeCache::Config::verify(AOTCodeCache* cache) const { - // First checks affect all cached AOT code -#ifdef ASSERT - if ((_flags & debugVM) == 0) { - log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created by product VM, it can't be used by debug VM"); - return false; - } -#else - if ((_flags & debugVM) != 0) { - log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created by debug VM, it can't be used by product VM"); - return false; - } +#define AOTCODECACHE_DISABLED_MSG "AOT Code Cache disabled: it was created with %s = " + +// Special case, print "GC = ..." to be more understandable. +inline void log_config_mismatch(CollectedHeap::Name saved, CollectedHeap::Name current, const char* name/*unused*/) { + load_failure_log().print_cr("AOT Code Cache disabled: it was created with GC = \"%s\" vs current \"%s\"", + GCConfig::hs_err_name(saved), GCConfig::hs_err_name(current)); +} + +inline void log_config_mismatch(bool saved, bool current, const char* name) { + load_failure_log().print_cr(AOTCODECACHE_DISABLED_MSG "%s vs current %s", name, + saved ? "true" : "false", current ? "true" : "false"); +} + +inline void log_config_mismatch(int saved, int current, const char* name) { + load_failure_log().print_cr(AOTCODECACHE_DISABLED_MSG "%d vs current %d", name, saved, current); +} + +inline void log_config_mismatch(uint saved, uint current, const char* name) { + load_failure_log().print_cr(AOTCODECACHE_DISABLED_MSG "%u vs current %u", name, saved, current); +} + +#ifdef _LP64 +inline void log_config_mismatch(intx saved, intx current, const char* name) { + load_failure_log().print_cr(AOTCODECACHE_DISABLED_MSG "%zd vs current %zd", name, saved, current); +} + +inline void log_config_mismatch(uintx saved, uintx current, const char* name) { + load_failure_log().print_cr(AOTCODECACHE_DISABLED_MSG "%zu vs current %zu", name, saved, current); +} #endif - CollectedHeap::Name aot_gc = (CollectedHeap::Name)_gc; - if (aot_gc != Universe::heap()->kind()) { - log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with different GC: %s vs current %s", GCConfig::hs_err_name(aot_gc), GCConfig::hs_err_name()); +template +bool check_config(T saved, T current, const char* name) { + if (saved != current) { + log_config_mismatch(saved, current, name); return false; + } else { + return true; } +} - if (((_flags & compressedClassPointers) != 0) != UseCompressedClassPointers) { - log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with UseCompressedClassPointers = %s", UseCompressedClassPointers ? "false" : "true"); - return false; - } - if (_compressedKlassShift != (uint)CompressedKlassPointers::shift()) { - log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with CompressedKlassPointers::shift() = %d vs current %d", _compressedKlassShift, CompressedKlassPointers::shift()); - return false; - } - - // The following checks do not affect AOT adapters caching - - if (((_flags & compressedOops) != 0) != UseCompressedOops) { - log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with UseCompressedOops = %s", UseCompressedOops ? "false" : "true"); - AOTStubCaching = false; - } - if (_compressedOopShift != (uint)CompressedOops::shift()) { - log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with different CompressedOops::shift(): %d vs current %d", _compressedOopShift, CompressedOops::shift()); - AOTStubCaching = false; - } - - // This should be the last check as it only disables AOTStubCaching - if ((_compressedOopBase == nullptr || CompressedOops::base() == nullptr) && (_compressedOopBase != CompressedOops::base())) { - log_debug(aot, codecache, init)("AOTStubCaching is disabled: incompatible CompressedOops::base(): %p vs current %p", _compressedOopBase, CompressedOops::base()); - AOTStubCaching = false; - } - +bool AOTCodeCache::Config::verify(AOTCodeCache* cache) const { + // check CPU features before checking flags that may be + // auto-configured in response to them if (!verify_cpu_features(cache)) { return false; } + + // Tests for config options which might affect validity of adapters, + // stubs or nmethods. Currently we take a pessemistic stand and + // drop the whole cache if any of these are changed. + +#define AOTCODECACHE_CHECK_VAR(type, name) \ + if (!check_config(_saved_ ## name, name, #name)) { return false; } +#define AOTCODECACHE_CHECK_FUN(type, name, fun) \ + if (!check_config(_saved_ ## name, fun, #fun)) { return false; } + + AOTCODECACHE_CONFIGS_DO(AOTCODECACHE_CHECK_VAR, AOTCODECACHE_CHECK_FUN); + + // Special configs that cannot be checked with macros + + if ((_compressedOopBase == nullptr || CompressedOops::base() == nullptr) && (_compressedOopBase != CompressedOops::base())) { + load_failure_log().print_cr("AOT Code Cache disabled: incompatible CompressedOops::base(): %p vs current %p", + _compressedOopBase, CompressedOops::base()); + return false; + } + +#if defined(X86) && !defined(ZERO) + // switching off UseUnalignedLoadStores can affect validity of fill + // stubs + if (_useUnalignedLoadStores && !UseUnalignedLoadStores) { + log_config_mismatch(_useUnalignedLoadStores, UseUnalignedLoadStores, "UseUnalignedLoadStores"); + return false; + } +#endif // defined(X86) && !defined(ZERO) + +#if defined(AARCH64) && !defined(ZERO) + // switching on AvoidUnalignedAccesses may affect validity of array + // copy stubs and nmethods + if (!_avoidUnalignedAccesses && AvoidUnalignedAccesses) { + log_config_mismatch(_avoidUnalignedAccesses, AvoidUnalignedAccesses, "AvoidUnalignedAccesses"); + return false; + } +#endif // defined(AARCH64) && !defined(ZERO) + return true; } bool AOTCodeCache::Header::verify(uint load_size) const { if (_version != AOT_CODE_VERSION) { - log_debug(aot, codecache, init)("AOT Code Cache disabled: different AOT Code version %d vs %d recorded in AOT Code header", AOT_CODE_VERSION, _version); + load_failure_log().print_cr("AOT Code Cache disabled: different AOT Code version %d vs %d recorded in AOT Code header", AOT_CODE_VERSION, _version); return false; } if (load_size < _cache_size) { - log_debug(aot, codecache, init)("AOT Code Cache disabled: AOT Code Cache size %d < %d recorded in AOT Code header", load_size, _cache_size); + load_failure_log().print_cr("AOT Code Cache disabled: AOT Code Cache size %d < %d recorded in AOT Code header", load_size, _cache_size); return false; } return true; @@ -546,6 +635,13 @@ AOTCodeReader::AOTCodeReader(AOTCodeCache* cache, AOTCodeEntry* entry) { _load_buffer = cache->cache_buffer(); _read_position = 0; _lookup_failed = false; + _name = nullptr; + _reloc_data = nullptr; + _reloc_count = 0; + _oop_maps = nullptr; + _entry_kind = AOTCodeEntry::None; + _stub_data = nullptr; + _id = -1; } void AOTCodeReader::set_read_position(uint pos) { @@ -763,6 +859,7 @@ bool AOTCodeCache::finish_write() { AOTCodeEntry* entries_address = _store_entries; // Pointer to latest entry uint adapters_count = 0; uint shared_blobs_count = 0; + uint stubgen_blobs_count = 0; uint C1_blobs_count = 0; uint C2_blobs_count = 0; uint max_size = 0; @@ -779,7 +876,7 @@ bool AOTCodeCache::finish_write() { current += size; uint n = write_bytes(&(entries_address[i]), sizeof(AOTCodeEntry)); if (n != sizeof(AOTCodeEntry)) { - FREE_C_HEAP_ARRAY(uint, search); + FREE_C_HEAP_ARRAY(search); return false; } search[entries_count*2 + 0] = entries_address[i].id(); @@ -790,6 +887,8 @@ bool AOTCodeCache::finish_write() { adapters_count++; } else if (kind == AOTCodeEntry::SharedBlob) { shared_blobs_count++; + } else if (kind == AOTCodeEntry::StubGenBlob) { + stubgen_blobs_count++; } else if (kind == AOTCodeEntry::C1Blob) { C1_blobs_count++; } else if (kind == AOTCodeEntry::C2Blob) { @@ -798,7 +897,7 @@ bool AOTCodeCache::finish_write() { } if (entries_count == 0) { log_info(aot, codecache, exit)("AOT Code Cache was not created: no entires"); - FREE_C_HEAP_ARRAY(uint, search); + FREE_C_HEAP_ARRAY(search); return true; // Nothing to write } assert(entries_count <= store_count, "%d > %d", entries_count, store_count); @@ -814,7 +913,7 @@ bool AOTCodeCache::finish_write() { qsort(search, entries_count, 2*sizeof(uint), uint_cmp); search_size = 2 * entries_count * sizeof(uint); copy_bytes((const char*)search, (address)current, search_size); - FREE_C_HEAP_ARRAY(uint, search); + FREE_C_HEAP_ARRAY(search); current += search_size; // Write entries @@ -826,6 +925,7 @@ bool AOTCodeCache::finish_write() { log_debug(aot, codecache, exit)(" Adapters: total=%u", adapters_count); log_debug(aot, codecache, exit)(" Shared Blobs: total=%d", shared_blobs_count); + log_debug(aot, codecache, exit)(" StubGen Blobs: total=%d", stubgen_blobs_count); log_debug(aot, codecache, exit)(" C1 Blobs: total=%d", C1_blobs_count); log_debug(aot, codecache, exit)(" C2 Blobs: total=%d", C2_blobs_count); log_debug(aot, codecache, exit)(" AOT code cache size: %u bytes, max entry's size: %u bytes", size, max_size); @@ -835,7 +935,8 @@ bool AOTCodeCache::finish_write() { header->init(size, (uint)strings_count, strings_offset, entries_count, new_entries_offset, adapters_count, shared_blobs_count, - C1_blobs_count, C2_blobs_count, cpu_features_offset); + stubgen_blobs_count, C1_blobs_count, + C2_blobs_count, cpu_features_offset); log_info(aot, codecache, exit)("Wrote %d AOT code entries to AOT Code Cache", entries_count); } @@ -844,19 +945,53 @@ bool AOTCodeCache::finish_write() { //------------------Store/Load AOT code ---------------------- -bool AOTCodeCache::store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind, uint id, const char* name) { +bool AOTCodeCache::store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind, uint id, const char* name, AOTStubData* stub_data, CodeBuffer* code_buffer) { + assert(AOTCodeEntry::is_valid_entry_kind(entry_kind), "invalid entry_kind %d", entry_kind); + + // we only expect stub data and a code buffer for a multi stub blob + assert(AOTCodeEntry::is_multi_stub_blob(entry_kind) == (stub_data != nullptr), + "entry_kind %d does not match stub_data pointer %p", + entry_kind, stub_data); + + assert((stub_data == nullptr) == (code_buffer == nullptr), + "stub data and code buffer must both be null or both non null"); + + // If this is a stub and the cache is on for either load or dump we + // need to insert the stub entries into the AOTCacheAddressTable so + // that relocs which refer to entries defined by this blob get + // translated correctly. + // + // Entry insertion needs to be be done up front before writing the + // blob because some blobs rely on internal daisy-chain references + // from one entry to another. + // + // Entry insertion also needs to be done even if the cache is open + // for use but not for dump. This may be needed when an archived + // blob omits some entries -- either because of a config change or a + // load failure -- with the result that the entries end up being + // generated. These generated entry addresses may be needed to + // resolve references from subsequently loaded blobs (for either + // stubs or nmethods). + + if (is_on() && AOTCodeEntry::is_blob(entry_kind)) { + publish_stub_addresses(blob, (BlobId)id, stub_data); + } + AOTCodeCache* cache = open_for_dump(); if (cache == nullptr) { return false; } - assert(AOTCodeEntry::is_valid_entry_kind(entry_kind), "invalid entry_kind %d", entry_kind); - if (AOTCodeEntry::is_adapter(entry_kind) && !is_dumping_adapter()) { return false; } if (AOTCodeEntry::is_blob(entry_kind) && !is_dumping_stub()) { return false; } + // we do not currently store C2 stubs because we are seeing weird + // memory errors when loading them -- see JDK-8357593 + if (entry_kind == AOTCodeEntry::C2Blob) { + return false; + } log_debug(aot, codecache, stubs)("Writing blob '%s' (id=%u, kind=%s) to AOT Code Cache", name, id, aot_code_entry_kind_name[entry_kind]); #ifdef ASSERT @@ -896,8 +1031,44 @@ bool AOTCodeCache::store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind } CodeBlob::archive_blob(&blob, archive_buffer); - uint reloc_data_size = blob.relocation_size(); - n = cache->write_bytes((address)blob.relocation_begin(), reloc_data_size); + // For a relocatable code blob its relocations are linked from the + // blob. However, for a non-relocatable (stubgen) blob we only have + // transient relocations attached to the code buffer that are added + // in order to support AOT-load time patching. in either case, we + // need to explicitly save these relocs when storing the blob to the + // archive so we can then reload them and reattach them to either + // the blob or to a code buffer when we reload the blob into a + // production JVM. + // + // Either way we are then in a position to iterate over the relocs + // and AOT patch the ones that refer to code that may move between + // assembly and production time. We also need to save and restore + // AOT address table indexes for the target addresses of affected + // relocs. That happens below. + + int reloc_count; + address reloc_data; + if (AOTCodeEntry::is_multi_stub_blob(entry_kind)) { + CodeSection* cs = code_buffer->code_section(CodeBuffer::SECT_INSTS); + reloc_count = (cs->has_locs() ? cs->locs_count() : 0); + reloc_data = (reloc_count > 0 ? (address)cs->locs_start() : nullptr); + } else { + reloc_count = blob.relocation_size() / sizeof(relocInfo); + reloc_data = (address)blob.relocation_begin(); + } + n = cache->write_bytes(&reloc_count, sizeof(int)); + if (n != sizeof(int)) { + return false; + } + if (AOTCodeEntry::is_multi_stub_blob(entry_kind)) { + // align to heap word size before writing the relocs so we can + // install them into a code buffer when they get restored + if (!cache->align_write()) { + return false; + } + } + uint reloc_data_size = (uint)(reloc_count * sizeof(relocInfo)); + n = cache->write_bytes(reloc_data, reloc_data_size); if (n != reloc_data_size) { return false; } @@ -910,8 +1081,40 @@ bool AOTCodeCache::store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind has_oop_maps = true; } + // In the case of a multi-stub blob we need to write start, end, + // secondary entries and extras. For any other blob entry addresses + // beyond the blob start will be stored in the blob as offsets. + if (stub_data != nullptr) { + if (!cache->write_stub_data(blob, stub_data)) { + return false; + } + } + + // now we have added all the other data we can write details of any + // extra the AOT relocations + + bool write_ok = true; + if (AOTCodeEntry::is_multi_stub_blob(entry_kind)) { + if (reloc_count > 0) { + CodeSection* cs = code_buffer->code_section(CodeBuffer::SECT_INSTS); + RelocIterator iter(cs); + write_ok = cache->write_relocations(blob, iter); + } + } else { + RelocIterator iter(&blob); + write_ok = cache->write_relocations(blob, iter); + } + + if (!write_ok) { + if (!cache->failed()) { + // We may miss an address in AOT table - skip this code blob. + cache->set_write_position(entry_position); + } + return false; + } + #ifndef PRODUCT - // Write asm remarks + // Write asm remarks after relocation info if (!cache->write_asm_remarks(blob)) { return false; } @@ -920,15 +1123,8 @@ bool AOTCodeCache::store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind } #endif /* PRODUCT */ - if (!cache->write_relocations(blob)) { - if (!cache->failed()) { - // We may miss an address in AOT table - skip this code blob. - cache->set_write_position(entry_position); - } - return false; - } - uint entry_size = cache->_write_position - entry_position; + AOTCodeEntry* entry = new(cache) AOTCodeEntry(entry_kind, encode_id(entry_kind, id), entry_position, entry_size, name_offset, name_size, blob_offset, has_oop_maps, blob.content_begin()); @@ -936,25 +1132,141 @@ bool AOTCodeCache::store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind return true; } -bool AOTCodeCache::store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind, BlobId id) { - assert(AOTCodeEntry::is_blob(entry_kind), - "wrong entry kind for blob id %s", StubInfo::name(id)); - return store_code_blob(blob, entry_kind, (uint)id, StubInfo::name(id)); +bool AOTCodeCache::store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind, uint id, const char* name) { + assert(!AOTCodeEntry::is_blob(entry_kind), + "wrong entry kind for numeric id %d", id); + return store_code_blob(blob, entry_kind, (uint)id, name, nullptr, nullptr); } -CodeBlob* AOTCodeCache::load_code_blob(AOTCodeEntry::Kind entry_kind, uint id, const char* name) { +bool AOTCodeCache::store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind, BlobId id) { + assert(AOTCodeEntry::is_single_stub_blob(entry_kind), + "wrong entry kind for blob id %s", StubInfo::name(id)); + return store_code_blob(blob, entry_kind, (uint)id, StubInfo::name(id), nullptr, nullptr); +} + +bool AOTCodeCache::store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind, BlobId id, AOTStubData* stub_data, CodeBuffer* code_buffer) { + assert(AOTCodeEntry::is_multi_stub_blob(entry_kind), + "wrong entry kind for multi stub blob id %s", StubInfo::name(id)); + return store_code_blob(blob, entry_kind, (uint)id, StubInfo::name(id), stub_data, code_buffer); +} + +bool AOTCodeCache::write_stub_data(CodeBlob &blob, AOTStubData *stub_data) { + BlobId blob_id = stub_data->blob_id(); + StubId stub_id = StubInfo::stub_base(blob_id); + address blob_base = blob.code_begin(); + int stub_cnt = StubInfo::stub_count(blob_id); + int n; + + LogStreamHandle(Trace, aot, codecache, stubs) log; + + if (log.is_enabled()) { + log.print_cr("======== Stub data starts at offset %d", _write_position); + } + + for (int i = 0; i < stub_cnt; i++, stub_id = StubInfo::next_in_blob(blob_id, stub_id)) { + // for each stub we find in the ranges list we write an int + // sequence where + // + // - start_pos is the stub start address encoded as a code section offset + // + // - end is the stub end address encoded as an offset from start + // + // - N counts the number of stub-local entries/extras + // + // - offseti is a stub-local entry/extra address encoded as len for + // a null address otherwise as an offset in range [1,len-1] + + StubAddrRange& range = stub_data->get_range(i); + GrowableArray
& addresses = stub_data->address_array(); + int base = range.start_index(); + if (base >= 0) { + n = write_bytes(&stub_id, sizeof(StubId)); + if (n != sizeof(StubId)) { + return false; + } + address start = addresses.at(base); + assert (blob_base <= start, "sanity"); + uint offset = (uint)(start - blob_base); + n = write_bytes(&offset, sizeof(uint)); + if (n != sizeof(int)) { + return false; + } + address end = addresses.at(base + 1); + assert (start < end, "sanity"); + offset = (uint)(end - start); + n = write_bytes(&offset, sizeof(uint)); + if (n != sizeof(int)) { + return false; + } + // write number of secondary and extra entries + int count = range.count() - 2; + n = write_bytes(&count, sizeof(int)); + if (n != sizeof(int)) { + return false; + } + for (int j = 0; j < count; j++) { + address next = addresses.at(base + 2 + j); + if (next != nullptr) { + // n.b. This maps next == end to the stub length which + // means we will reconstitute the address as nullptr. That + // happens when we have a handler range covers the end of + // a stub and needs to be handled specially by the client + // that restores the extras. + assert(start <= next && next <= end, "sanity"); + offset = (uint)(next - start); + } else { + // this can happen when a stub is not generated or an + // extra is the common handler target + offset = NULL_ADDRESS_MARKER; + } + n = write_bytes(&offset, sizeof(uint)); + if (n != sizeof(int)) { + return false; + } + } + if (log.is_enabled()) { + log.print_cr("======== wrote stub %s and %d addresses up to offset %d", + StubInfo::name(stub_id), range.count(), _write_position); + } + } + } + // we should have exhausted all stub ids in the blob + assert(stub_id == StubId::NO_STUBID, "sanity"); + // write NO_STUBID as an end marker + n = write_bytes(&stub_id, sizeof(StubId)); + if (n != sizeof(StubId)) { + return false; + } + + if (log.is_enabled()) { + log.print_cr("======== Stub data ends at offset %d", _write_position); + } + + return true; +} + +CodeBlob* AOTCodeCache::load_code_blob(AOTCodeEntry::Kind entry_kind, uint id, const char* name, AOTStubData* stub_data) { AOTCodeCache* cache = open_for_use(); if (cache == nullptr) { return nullptr; } assert(AOTCodeEntry::is_valid_entry_kind(entry_kind), "invalid entry_kind %d", entry_kind); + assert(AOTCodeEntry::is_multi_stub_blob(entry_kind) == (stub_data != nullptr), + "entry_kind %d does not match stub_data pointer %p", + entry_kind, stub_data); + if (AOTCodeEntry::is_adapter(entry_kind) && !is_using_adapter()) { return nullptr; } if (AOTCodeEntry::is_blob(entry_kind) && !is_using_stub()) { return nullptr; } + // we do not currently load C2 stubs because we are seeing weird + // memory errors when loading them -- see JDK-8357593 + if (entry_kind == AOTCodeEntry::C2Blob) { + return nullptr; + } log_debug(aot, codecache, stubs)("Reading blob '%s' (id=%u, kind=%s) from AOT Code Cache", name, id, aot_code_entry_kind_name[entry_kind]); AOTCodeEntry* entry = cache->find_entry(entry_kind, encode_id(entry_kind, id)); @@ -962,20 +1274,32 @@ CodeBlob* AOTCodeCache::load_code_blob(AOTCodeEntry::Kind entry_kind, uint id, c return nullptr; } AOTCodeReader reader(cache, entry); - CodeBlob* blob = reader.compile_code_blob(name); + CodeBlob* blob = reader.compile_code_blob(name, entry_kind, id, stub_data); log_debug(aot, codecache, stubs)("%sRead blob '%s' (id=%u, kind=%s) from AOT Code Cache", (blob == nullptr? "Failed to " : ""), name, id, aot_code_entry_kind_name[entry_kind]); return blob; } -CodeBlob* AOTCodeCache::load_code_blob(AOTCodeEntry::Kind entry_kind, BlobId id) { - assert(AOTCodeEntry::is_blob(entry_kind), - "wrong entry kind for blob id %s", StubInfo::name(id)); - return load_code_blob(entry_kind, (uint)id, StubInfo::name(id)); +CodeBlob* AOTCodeCache::load_code_blob(AOTCodeEntry::Kind entry_kind, uint id, const char* name) { + assert(!AOTCodeEntry::is_blob(entry_kind), + "wrong entry kind for numeric id %d", id); + return load_code_blob(entry_kind, (uint)id, name, nullptr); } -CodeBlob* AOTCodeReader::compile_code_blob(const char* name) { +CodeBlob* AOTCodeCache::load_code_blob(AOTCodeEntry::Kind entry_kind, BlobId id) { + assert(AOTCodeEntry::is_single_stub_blob(entry_kind), + "wrong entry kind for blob id %s", StubInfo::name(id)); + return load_code_blob(entry_kind, (uint)id, StubInfo::name(id), nullptr); +} + +CodeBlob* AOTCodeCache::load_code_blob(AOTCodeEntry::Kind entry_kind, BlobId id, AOTStubData* stub_data) { + assert(AOTCodeEntry::is_multi_stub_blob(entry_kind), + "wrong entry kind for blob id %s", StubInfo::name(id)); + return load_code_blob(entry_kind, (uint)id, StubInfo::name(id), stub_data); +} + +CodeBlob* AOTCodeReader::compile_code_blob(const char* name, AOTCodeEntry::Kind entry_kind, int id, AOTStubData* stub_data) { uint entry_position = _entry->offset(); // Read name @@ -989,39 +1313,40 @@ CodeBlob* AOTCodeReader::compile_code_blob(const char* name) { set_lookup_failed(); // Skip this blob return nullptr; } + _name = stored_name; - // Read archived code blob + // Read archived code blob and related info uint offset = entry_position + _entry->blob_offset(); CodeBlob* archived_blob = (CodeBlob*)addr(offset); offset += archived_blob->size(); - address reloc_data = (address)addr(offset); - offset += archived_blob->relocation_size(); + _reloc_count = *(int*)addr(offset); offset += sizeof(int); + if (AOTCodeEntry::is_multi_stub_blob(entry_kind)) { + // position of relocs will have been aligned to heap word size so + // we can install them into a code buffer + offset = align_up(offset, DATA_ALIGNMENT); + } + _reloc_data = (address)addr(offset); + offset += _reloc_count * sizeof(relocInfo); set_read_position(offset); - ImmutableOopMapSet* oop_maps = nullptr; if (_entry->has_oop_maps()) { - oop_maps = read_oop_map_set(); + _oop_maps = read_oop_map_set(); } - CodeBlob* code_blob = CodeBlob::create(archived_blob, - stored_name, - reloc_data, - oop_maps - ); + // record current context for use by that callback + _stub_data = stub_data; + _entry_kind = entry_kind; + _id = id; + + // CodeBlob::restore() calls AOTCodeReader::restore() + + CodeBlob* code_blob = CodeBlob::create(archived_blob, this); + if (code_blob == nullptr) { // no space left in CodeCache return nullptr; } -#ifndef PRODUCT - code_blob->asm_remarks().init(); - read_asm_remarks(code_blob->asm_remarks()); - code_blob->dbg_strings().init(); - read_dbg_strings(code_blob->dbg_strings()); -#endif // PRODUCT - - fix_relocations(code_blob); - #ifdef ASSERT LogStreamHandle(Trace, aot, codecache, stubs) log; if (log.is_enabled()) { @@ -1032,15 +1357,221 @@ CodeBlob* AOTCodeReader::compile_code_blob(const char* name) { return code_blob; } +void AOTCodeReader::restore(CodeBlob* code_blob) { + precond(AOTCodeCache::is_on_for_use()); + precond(_name != nullptr); + precond(_reloc_data != nullptr); + + code_blob->set_name(_name); + // Saved relocations need restoring except for the case of a + // multi-stub blob which has no runtime relocations. However, we may + // still have saved some (re-)load time relocs that were attached to + // the generator's code buffer. We don't attach them to the blob but + // they get processed below by fix_relocations. + if (!AOTCodeEntry::is_multi_stub_blob(_entry_kind)) { + code_blob->restore_mutable_data(_reloc_data); + } + code_blob->set_oop_maps(_oop_maps); + + // if this is a multi stub blob load its entries + if (AOTCodeEntry::is_blob(_entry_kind)) { + BlobId blob_id = static_cast(_id); + if (StubInfo::is_stubgen(blob_id)) { + assert(_stub_data != nullptr, "sanity"); + read_stub_data(code_blob, _stub_data); + } + // publish entries found either in stub_data or as offsets in blob + AOTCodeCache::publish_stub_addresses(*code_blob, blob_id, _stub_data); + } + + // Now that all the entry points are in the address table we can + // read all the extra reloc info and fix up any addresses that need + // patching to adjust for a new location in a new JVM. We can be + // sure to correctly update all runtime references, including + // cross-linked stubs that are internally daisy-chained. If + // relocation fails and we have to re-generate any of the stubs then + // the entry points for newly generated stubs will get updated, + // ensuring that any other stubs or nmethods we need to relocate + // will use the correct address. + + // if we have a relocatable code blob then the relocs are already + // attached to the blob and we can iterate over it to find the ones + // we need to patch. With a non-relocatable code blob we need to + // wrap it with a CodeBuffer and then reattach the relocs to the + // code buffer. + + if (AOTCodeEntry::is_multi_stub_blob(_entry_kind)) { + // the blob doesn't have any proper runtime relocs but we can + // reinstate the AOT-load time relocs we saved from the code + // buffer that generated this blob in a new code buffer and use + // the latter to iterate over them + if (_reloc_count > 0) { + CodeBuffer code_buffer(code_blob); + relocInfo* locs = (relocInfo*)_reloc_data; + code_buffer.insts()->initialize_shared_locs(locs, _reloc_count); + code_buffer.insts()->set_locs_end(locs + _reloc_count); + CodeSection *cs = code_buffer.code_section(CodeBuffer::SECT_INSTS); + RelocIterator reloc_iter(cs); + fix_relocations(code_blob, reloc_iter); + } + } else { + // the AOT-load time relocs will be in the blob's restored relocs + RelocIterator reloc_iter(code_blob); + fix_relocations(code_blob, reloc_iter); + } + +#ifndef PRODUCT + code_blob->asm_remarks().init(); + read_asm_remarks(code_blob->asm_remarks()); + code_blob->dbg_strings().init(); + read_dbg_strings(code_blob->dbg_strings()); +#endif // PRODUCT +} + +void AOTCodeReader::read_stub_data(CodeBlob* code_blob, AOTStubData* stub_data) { + GrowableArray
& addresses = stub_data->address_array(); + // Read the list of stub ids and associated start, end, secondary + // and extra addresses and install them in the stub data. + // + // Also insert all start and secondary addresses into the AOTCache + // address table so we correctly relocate this blob and any followng + // blobs/nmethods. + // + // n.b. if an error occurs and we need to regenerate any of these + // stubs the address table will be updated as a side-effect of + // regeneration. + + address blob_base = code_blob->code_begin(); + uint blob_size = (uint)(code_blob->code_end() - blob_base); + int offset = read_position(); + LogStreamHandle(Trace, aot, codecache, stubs) log; + if (log.is_enabled()) { + log.print_cr("======== Stub data starts at offset %d", offset); + } + // read stub and entries until we see NO_STUBID + StubId stub_id = *(StubId*)addr(offset); offset += sizeof(StubId); + // we ought to have at least one saved stub in the blob + assert(stub_id != StubId::NO_STUBID, "blob %s contains no stubs!", StubInfo::name(stub_data->blob_id())); + while (stub_id != StubId::NO_STUBID) { + assert(StubInfo::blob(stub_id) == stub_data->blob_id(), "sanity"); + int idx = StubInfo::stubgen_offset_in_blob(stub_data->blob_id(), stub_id); + StubAddrRange& range = stub_data->get_range(idx); + // we should only see a stub once + assert(range.start_index() < 0, "repeated entry for stub %s", StubInfo::name(stub_id)); + int address_base = addresses.length(); + // start is an offset from the blob base + uint start = *(uint*)addr(offset); offset += sizeof(uint); + assert(start < blob_size, "stub %s start offset %d exceeds buffer length %d", StubInfo::name(stub_id), start, blob_size); + address stub_start = blob_base + start; + addresses.append(stub_start); + // end is an offset from the stub start + uint end = *(uint*)addr(offset); offset += sizeof(uint); + assert(start + end <= blob_size, "stub %s end offset %d exceeds remaining buffer length %d", StubInfo::name(stub_id), end, blob_size - start); + addresses.append(stub_start + end); + // read count of secondary entries plus extras + int entries_count = *(int*)addr(offset); offset += sizeof(int); + assert(entries_count >= (StubInfo::entry_count(stub_id) - 1), "not enough entries for %s", StubInfo::name(stub_id)); + for (int i = 0; i < entries_count; i++) { + // entry offset is an offset from the stub start less than or + // equal to end + uint entry = *(uint*)addr(offset); offset += sizeof(uint); + if (entry <= end) { + // entry addresses may not address end but extras can + assert(entry < end || i >= StubInfo::entry_count(stub_id), + "entry offset 0x%x exceeds stub length 0x%x for stub %s", + entry, end, StubInfo::name(stub_id)); + addresses.append(stub_start + entry); + } else { + // special case: entry encodes a nullptr + assert(entry == AOTCodeCache::NULL_ADDRESS_MARKER, "stub %s entry offset %d lies beyond stub end %d and does not equal NULL_ADDRESS_MARKER", StubInfo::name(stub_id), entry, end); + addresses.append(nullptr); + } + } + if (log.is_enabled()) { + log.print_cr("======== read stub %s and %d addresses up to offset %d", + StubInfo::name(stub_id), 2 + entries_count, offset); + } + range.init_entry(address_base, 2 + entries_count); + // move on to next stub or NO_STUBID + stub_id = *(StubId*)addr(offset); offset += sizeof(StubId); + } + if (log.is_enabled()) { + log.print_cr("======== Stub data ends at offset %d", offset); + } + + set_read_position(offset); +} + +void AOTCodeCache::publish_external_addresses(GrowableArray
& addresses) { + DEBUG_ONLY( _passed_init2 = true; ) + if (opened_cache == nullptr) { + return; + } + + cache()->_table->add_external_addresses(addresses); +} + +void AOTCodeCache::publish_stub_addresses(CodeBlob &code_blob, BlobId blob_id, AOTStubData *stub_data) { + if (stub_data != nullptr) { + // register all entries in stub + assert(StubInfo::stub_count(blob_id) > 1, + "multiple stub data provided for single stub blob %s", + StubInfo::name(blob_id)); + assert(blob_id == stub_data->blob_id(), + "blob id %s does not match id in stub data %s", + StubInfo::name(blob_id), + StubInfo::name(stub_data->blob_id())); + // iterate over all stubs in the blob + StubId stub_id = StubInfo::stub_base(blob_id); + int stub_cnt = StubInfo::stub_count(blob_id); + GrowableArray
& addresses = stub_data->address_array(); + for (int i = 0; i < stub_cnt; i++) { + assert(stub_id != StubId::NO_STUBID, "sanity"); + StubAddrRange& range = stub_data->get_range(i); + int base = range.start_index(); + if (base >= 0) { + cache()->add_stub_entries(stub_id, addresses.at(base), &addresses, base + 2); + } + stub_id = StubInfo::next_in_blob(blob_id, stub_id); + } + // we should have exhausted all stub ids in the blob + assert(stub_id == StubId::NO_STUBID, "sanity"); + } else { + // register entry or entries for a single stub blob + StubId stub_id = StubInfo::stub_base(blob_id); + assert(StubInfo::stub_count(blob_id) == 1, + "multiple stub blob %s provided without stub data", + StubInfo::name(blob_id)); + address start = code_blob.code_begin(); + if (StubInfo::entry_count(stub_id) == 1) { + assert(!code_blob.is_deoptimization_stub(), "expecting multiple entries for stub %s", StubInfo::name(stub_id)); + // register the blob base address as the only entry + cache()->add_stub_entries(stub_id, start); + } else { + assert(code_blob.is_deoptimization_stub(), "only expecting one entry for stub %s", StubInfo::name(stub_id)); + DeoptimizationBlob *deopt_blob = code_blob.as_deoptimization_blob(); + assert(deopt_blob->unpack() == start, "unexpected offset 0x%x for deopt stub entry", (int)(deopt_blob->unpack() - start)); + GrowableArray
addresses; + addresses.append(deopt_blob->unpack_with_exception()); + addresses.append(deopt_blob->unpack_with_reexecution()); + addresses.append(deopt_blob->unpack_with_exception_in_tls()); +#if INCLUDE_JVMCI + addresses.append(deopt_blob->uncommon_trap()); + addresses.append(deopt_blob->implicit_exception_uncommon_trap()); +#endif // INCLUDE_JVMCI + cache()->add_stub_entries(stub_id, start, &addresses, 0); + } + } +} + // ------------ process code and data -------------- // Can't use -1. It is valid value for jump to iteself destination // used by static call stub: see NativeJump::jump_destination(). #define BAD_ADDRESS_ID -2 -bool AOTCodeCache::write_relocations(CodeBlob& code_blob) { +bool AOTCodeCache::write_relocations(CodeBlob& code_blob, RelocIterator& iter) { GrowableArray reloc_data; - RelocIterator iter(&code_blob); LogStreamHandle(Trace, aot, codecache, reloc) log; while (iter.next()) { int idx = reloc_data.append(0); // default value @@ -1094,6 +1625,11 @@ bool AOTCodeCache::write_relocations(CodeBlob& code_blob) { // Write the count first int count = reloc_data.length(); write_bytes(&count, sizeof(int)); + if (log.is_enabled()) { + log.print_cr("======== extra relocations count=%d", count); + log.print( " {"); + } + bool first = true; for (GrowableArrayIterator iter = reloc_data.begin(); iter != reloc_data.end(); ++iter) { uint value = *iter; @@ -1101,23 +1637,43 @@ bool AOTCodeCache::write_relocations(CodeBlob& code_blob) { if (n != sizeof(uint)) { return false; } + if (log.is_enabled()) { + if (first) { + first = false; + log.print("%d", value); + } else { + log.print(", %d", value); + } + } + } + if (log.is_enabled()) { + log.print_cr("}"); } return true; } -void AOTCodeReader::fix_relocations(CodeBlob* code_blob) { - LogStreamHandle(Trace, aot, reloc) log; +void AOTCodeReader::fix_relocations(CodeBlob *code_blob, RelocIterator& iter) { uint offset = read_position(); - int count = *(int*)addr(offset); + int reloc_count = *(int*)addr(offset); offset += sizeof(int); - if (log.is_enabled()) { - log.print_cr("======== extra relocations count=%d", count); - } uint* reloc_data = (uint*)addr(offset); - offset += (count * sizeof(uint)); + offset += (reloc_count * sizeof(uint)); set_read_position(offset); - RelocIterator iter(code_blob); + LogStreamHandle(Trace, aot, codecache, reloc) log; + if (log.is_enabled()) { + log.print_cr("======== extra relocations count=%d", reloc_count); + log.print(" {"); + for(int i = 0; i < reloc_count; i++) { + if (i == 0) { + log.print("%d", reloc_data[i]); + } else { + log.print(", %d", reloc_data[i]); + } + } + log.print_cr("}"); + } + int j = 0; while (iter.next()) { switch (iter.type()) { @@ -1166,7 +1722,7 @@ void AOTCodeReader::fix_relocations(CodeBlob* code_blob) { } j++; } - assert(j == count, "sanity"); + assert(j == reloc_count, "sanity"); } bool AOTCodeCache::write_oop_map_set(CodeBlob& cb) { @@ -1276,254 +1832,359 @@ void AOTCodeReader::read_dbg_strings(DbgStrings& dbg_strings) { //======================= AOTCodeAddressTable =============== -// address table ids for generated routines, external addresses and C -// string addresses are partitioned into positive integer ranges -// defined by the following positive base and max values -// i.e. [_extrs_base, _extrs_base + _extrs_max -1], -// [_blobs_base, _blobs_base + _blobs_max -1], -// ... -// [_c_str_base, _c_str_base + _c_str_max -1], +// address table ids for generated routine entry adresses, external +// addresses and C string addresses are partitioned into positive +// integer ranges defined by the following positive base and max +// values i.e. [_extrs_base, _extrs_base + _extrs_max -1], +// [_stubs_base, _stubs_base + _stubs_max -1], [_c_str_base, +// _c_str_base + _c_str_max -1], -#define _extrs_max 100 -#define _stubs_max 3 - -#define _shared_blobs_max 20 -#define _C1_blobs_max 10 -#define _blobs_max (_shared_blobs_max+_C1_blobs_max) -#define _all_max (_extrs_max+_stubs_max+_blobs_max) +#define _extrs_max 380 +#define _stubs_max static_cast(EntryId::NUM_ENTRYIDS) #define _extrs_base 0 #define _stubs_base (_extrs_base + _extrs_max) -#define _shared_blobs_base (_stubs_base + _stubs_max) -#define _C1_blobs_base (_shared_blobs_base + _shared_blobs_max) -#define _blobs_end (_shared_blobs_base + _blobs_max) +#define _all_max (_stubs_base + _stubs_max) -#define SET_ADDRESS(type, addr) \ - { \ - type##_addr[type##_length++] = (address) (addr); \ - assert(type##_length <= type##_max, "increase size"); \ +// setter for external addresses and string addresses inserts new +// addresses in the order they are encountered them which must remain +// the same across an assembly run and subsequent production run + +#define ADD_EXTERNAL_ADDRESS(addr) \ + { \ + hash_address((address) addr, _extrs_base + _extrs_length); \ + _extrs_addr[_extrs_length++] = (address) (addr); \ + assert(_extrs_length <= _extrs_max, "increase size"); \ } +// insert into to the address hash table the index of an external +// address or a stub address in the list of external or stub +// addresses, respectively, keyed by the relevant address + +void AOTCodeAddressTable::hash_address(address addr, int idx) { + // only do this if we have a non-null address to record and the + // cache is open for dumping + if (addr == nullptr) { + return; + } + // check opened_cache because this can be called before the cache is + // properly initialized and only continue when dumping is enabled + if (opened_cache != nullptr && opened_cache->for_dump()) { + if (_hash_table == nullptr) { + _hash_table = new (mtCode) AOTCodeAddressHashTable(); + } + assert(_hash_table->get(addr) == nullptr, "repeated insert of address " INTPTR_FORMAT, p2i(addr)); + _hash_table->put(addr, idx); + log_trace(aot, codecache)("Address " INTPTR_FORMAT " inserted into AOT Code Cache address hash table with index '%d'", + p2i(addr), idx); + } +} + static bool initializing_extrs = false; void AOTCodeAddressTable::init_extrs() { if (_extrs_complete || initializing_extrs) return; // Done already - assert(_blobs_end <= _all_max, "AOTCodeAddress table ranges need adjusting"); - initializing_extrs = true; _extrs_addr = NEW_C_HEAP_ARRAY(address, _extrs_max, mtCode); _extrs_length = 0; + { + // Required by initial stubs + ADD_EXTERNAL_ADDRESS(SharedRuntime::exception_handler_for_return_address); // used by forward_exception + ADD_EXTERNAL_ADDRESS(CompressedOops::base_addr()); // used by call_stub + ADD_EXTERNAL_ADDRESS(Thread::current); // used by call_stub + ADD_EXTERNAL_ADDRESS(SharedRuntime::throw_StackOverflowError); + ADD_EXTERNAL_ADDRESS(SharedRuntime::throw_delayed_StackOverflowError); + } + // Record addresses of VM runtime methods - SET_ADDRESS(_extrs, SharedRuntime::fixup_callers_callsite); - SET_ADDRESS(_extrs, SharedRuntime::handle_wrong_method); - SET_ADDRESS(_extrs, SharedRuntime::handle_wrong_method_abstract); - SET_ADDRESS(_extrs, SharedRuntime::handle_wrong_method_ic_miss); + ADD_EXTERNAL_ADDRESS(SharedRuntime::fixup_callers_callsite); + ADD_EXTERNAL_ADDRESS(SharedRuntime::handle_wrong_method); + ADD_EXTERNAL_ADDRESS(SharedRuntime::handle_wrong_method_abstract); + ADD_EXTERNAL_ADDRESS(SharedRuntime::handle_wrong_method_ic_miss); #if defined(AARCH64) && !defined(ZERO) - SET_ADDRESS(_extrs, JavaThread::aarch64_get_thread_helper); + ADD_EXTERNAL_ADDRESS(JavaThread::aarch64_get_thread_helper); + ADD_EXTERNAL_ADDRESS(BarrierSetAssembler::patching_epoch_addr()); #endif + +#ifndef PRODUCT + ADD_EXTERNAL_ADDRESS(&SharedRuntime::_jbyte_array_copy_ctr); // used by arraycopy stub on arm32 and x86_64 + ADD_EXTERNAL_ADDRESS(&SharedRuntime::_jshort_array_copy_ctr); // used by arraycopy stub + ADD_EXTERNAL_ADDRESS(&SharedRuntime::_jint_array_copy_ctr); // used by arraycopy stub + ADD_EXTERNAL_ADDRESS(&SharedRuntime::_jlong_array_copy_ctr); // used by arraycopy stub + ADD_EXTERNAL_ADDRESS(&SharedRuntime::_oop_array_copy_ctr); // used by arraycopy stub + ADD_EXTERNAL_ADDRESS(&SharedRuntime::_checkcast_array_copy_ctr); // used by arraycopy stub + ADD_EXTERNAL_ADDRESS(&SharedRuntime::_unsafe_array_copy_ctr); // used by arraycopy stub + ADD_EXTERNAL_ADDRESS(&SharedRuntime::_generic_array_copy_ctr); // used by arraycopy stub + ADD_EXTERNAL_ADDRESS(&SharedRuntime::_unsafe_set_memory_ctr); // used by arraycopy stub +#endif /* PRODUCT */ + + ADD_EXTERNAL_ADDRESS(SharedRuntime::enable_stack_reserved_zone); + +#if defined(AMD64) && !defined(ZERO) + ADD_EXTERNAL_ADDRESS(SharedRuntime::montgomery_multiply); + ADD_EXTERNAL_ADDRESS(SharedRuntime::montgomery_square); +#endif // defined(AMD64) && !defined(ZERO) + + ADD_EXTERNAL_ADDRESS(SharedRuntime::d2f); + ADD_EXTERNAL_ADDRESS(SharedRuntime::d2i); + ADD_EXTERNAL_ADDRESS(SharedRuntime::d2l); + ADD_EXTERNAL_ADDRESS(SharedRuntime::dcos); + ADD_EXTERNAL_ADDRESS(SharedRuntime::dexp); + ADD_EXTERNAL_ADDRESS(SharedRuntime::dlog); + ADD_EXTERNAL_ADDRESS(SharedRuntime::dlog10); + ADD_EXTERNAL_ADDRESS(SharedRuntime::dpow); +#ifndef ZERO + ADD_EXTERNAL_ADDRESS(SharedRuntime::drem); +#endif + ADD_EXTERNAL_ADDRESS(SharedRuntime::dsin); + ADD_EXTERNAL_ADDRESS(SharedRuntime::dtan); + ADD_EXTERNAL_ADDRESS(SharedRuntime::f2i); + ADD_EXTERNAL_ADDRESS(SharedRuntime::f2l); +#ifndef ZERO + ADD_EXTERNAL_ADDRESS(SharedRuntime::frem); +#endif + ADD_EXTERNAL_ADDRESS(SharedRuntime::l2d); + ADD_EXTERNAL_ADDRESS(SharedRuntime::l2f); + ADD_EXTERNAL_ADDRESS(SharedRuntime::ldiv); + ADD_EXTERNAL_ADDRESS(SharedRuntime::lmul); + ADD_EXTERNAL_ADDRESS(SharedRuntime::lrem); + +#if INCLUDE_JVMTI + ADD_EXTERNAL_ADDRESS(&JvmtiExport::_should_notify_object_alloc); +#endif /* INCLUDE_JVMTI */ + + ADD_EXTERNAL_ADDRESS(ThreadIdentifier::unsafe_offset()); + // already added + // ADD_EXTERNAL_ADDRESS(Thread::current); + + ADD_EXTERNAL_ADDRESS(os::javaTimeMillis); + ADD_EXTERNAL_ADDRESS(os::javaTimeNanos); +#ifndef PRODUCT + ADD_EXTERNAL_ADDRESS(os::breakpoint); +#endif + + ADD_EXTERNAL_ADDRESS(StubRoutines::crc_table_addr()); +#ifndef PRODUCT + ADD_EXTERNAL_ADDRESS(&SharedRuntime::_partial_subtype_ctr); +#endif + +#if INCLUDE_JFR + ADD_EXTERNAL_ADDRESS(JfrIntrinsicSupport::write_checkpoint); + ADD_EXTERNAL_ADDRESS(JfrIntrinsicSupport::return_lease); +#endif + + ADD_EXTERNAL_ADDRESS(UpcallLinker::handle_uncaught_exception); // used by upcall_stub_exception_handler + { // Required by Shared blobs - SET_ADDRESS(_extrs, Deoptimization::fetch_unroll_info); - SET_ADDRESS(_extrs, Deoptimization::unpack_frames); - SET_ADDRESS(_extrs, SafepointSynchronize::handle_polling_page_exception); - SET_ADDRESS(_extrs, SharedRuntime::resolve_opt_virtual_call_C); - SET_ADDRESS(_extrs, SharedRuntime::resolve_virtual_call_C); - SET_ADDRESS(_extrs, SharedRuntime::resolve_static_call_C); - SET_ADDRESS(_extrs, SharedRuntime::throw_StackOverflowError); - SET_ADDRESS(_extrs, SharedRuntime::throw_delayed_StackOverflowError); - SET_ADDRESS(_extrs, SharedRuntime::throw_AbstractMethodError); - SET_ADDRESS(_extrs, SharedRuntime::throw_IncompatibleClassChangeError); - SET_ADDRESS(_extrs, SharedRuntime::throw_NullPointerException_at_call); + ADD_EXTERNAL_ADDRESS(Deoptimization::fetch_unroll_info); + ADD_EXTERNAL_ADDRESS(Deoptimization::unpack_frames); + ADD_EXTERNAL_ADDRESS(SafepointSynchronize::handle_polling_page_exception); + ADD_EXTERNAL_ADDRESS(SharedRuntime::resolve_opt_virtual_call_C); + ADD_EXTERNAL_ADDRESS(SharedRuntime::resolve_virtual_call_C); + ADD_EXTERNAL_ADDRESS(SharedRuntime::resolve_static_call_C); + // already added + // ADD_EXTERNAL_ADDRESS(SharedRuntime::throw_delayed_StackOverflowError); + ADD_EXTERNAL_ADDRESS(SharedRuntime::throw_AbstractMethodError); + ADD_EXTERNAL_ADDRESS(SharedRuntime::throw_IncompatibleClassChangeError); + ADD_EXTERNAL_ADDRESS(SharedRuntime::throw_NullPointerException_at_call); } #ifdef COMPILER1 { // Required by C1 blobs - SET_ADDRESS(_extrs, static_cast(SharedRuntime::dtrace_object_alloc)); - SET_ADDRESS(_extrs, SharedRuntime::exception_handler_for_return_address); - SET_ADDRESS(_extrs, SharedRuntime::register_finalizer); - SET_ADDRESS(_extrs, Runtime1::is_instance_of); - SET_ADDRESS(_extrs, Runtime1::exception_handler_for_pc); - SET_ADDRESS(_extrs, Runtime1::check_abort_on_vm_exception); - SET_ADDRESS(_extrs, Runtime1::new_instance); - SET_ADDRESS(_extrs, Runtime1::counter_overflow); - SET_ADDRESS(_extrs, Runtime1::new_type_array); - SET_ADDRESS(_extrs, Runtime1::new_object_array); - SET_ADDRESS(_extrs, Runtime1::new_multi_array); - SET_ADDRESS(_extrs, Runtime1::throw_range_check_exception); - SET_ADDRESS(_extrs, Runtime1::throw_index_exception); - SET_ADDRESS(_extrs, Runtime1::throw_div0_exception); - SET_ADDRESS(_extrs, Runtime1::throw_null_pointer_exception); - SET_ADDRESS(_extrs, Runtime1::throw_array_store_exception); - SET_ADDRESS(_extrs, Runtime1::throw_class_cast_exception); - SET_ADDRESS(_extrs, Runtime1::throw_incompatible_class_change_error); - SET_ADDRESS(_extrs, Runtime1::is_instance_of); - SET_ADDRESS(_extrs, Runtime1::monitorenter); - SET_ADDRESS(_extrs, Runtime1::monitorexit); - SET_ADDRESS(_extrs, Runtime1::deoptimize); - SET_ADDRESS(_extrs, Runtime1::access_field_patching); - SET_ADDRESS(_extrs, Runtime1::move_klass_patching); - SET_ADDRESS(_extrs, Runtime1::move_mirror_patching); - SET_ADDRESS(_extrs, Runtime1::move_appendix_patching); - SET_ADDRESS(_extrs, Runtime1::predicate_failed_trap); - SET_ADDRESS(_extrs, Runtime1::unimplemented_entry); - SET_ADDRESS(_extrs, Thread::current); - SET_ADDRESS(_extrs, CompressedKlassPointers::base_addr()); -#ifndef PRODUCT - SET_ADDRESS(_extrs, os::breakpoint); -#endif + ADD_EXTERNAL_ADDRESS(static_cast(SharedRuntime::dtrace_object_alloc)); + ADD_EXTERNAL_ADDRESS(SharedRuntime::register_finalizer); + ADD_EXTERNAL_ADDRESS(Runtime1::is_instance_of); + ADD_EXTERNAL_ADDRESS(Runtime1::exception_handler_for_pc); + ADD_EXTERNAL_ADDRESS(Runtime1::check_abort_on_vm_exception); + ADD_EXTERNAL_ADDRESS(Runtime1::new_instance); + ADD_EXTERNAL_ADDRESS(Runtime1::counter_overflow); + ADD_EXTERNAL_ADDRESS(Runtime1::new_type_array); + ADD_EXTERNAL_ADDRESS(Runtime1::new_object_array); + ADD_EXTERNAL_ADDRESS(Runtime1::new_multi_array); + ADD_EXTERNAL_ADDRESS(Runtime1::throw_range_check_exception); + ADD_EXTERNAL_ADDRESS(Runtime1::throw_index_exception); + ADD_EXTERNAL_ADDRESS(Runtime1::throw_div0_exception); + ADD_EXTERNAL_ADDRESS(Runtime1::throw_null_pointer_exception); + ADD_EXTERNAL_ADDRESS(Runtime1::throw_array_store_exception); + ADD_EXTERNAL_ADDRESS(Runtime1::throw_class_cast_exception); + ADD_EXTERNAL_ADDRESS(Runtime1::throw_incompatible_class_change_error); + ADD_EXTERNAL_ADDRESS(Runtime1::monitorenter); + ADD_EXTERNAL_ADDRESS(Runtime1::monitorexit); + ADD_EXTERNAL_ADDRESS(Runtime1::deoptimize); + ADD_EXTERNAL_ADDRESS(Runtime1::access_field_patching); + ADD_EXTERNAL_ADDRESS(Runtime1::move_klass_patching); + ADD_EXTERNAL_ADDRESS(Runtime1::move_mirror_patching); + ADD_EXTERNAL_ADDRESS(Runtime1::move_appendix_patching); + ADD_EXTERNAL_ADDRESS(Runtime1::predicate_failed_trap); + ADD_EXTERNAL_ADDRESS(Runtime1::unimplemented_entry); + // already added + // ADD_EXTERNAL_ADDRESS(Thread::current); + ADD_EXTERNAL_ADDRESS(CompressedKlassPointers::base_addr()); } #endif #ifdef COMPILER2 { // Required by C2 blobs - SET_ADDRESS(_extrs, Deoptimization::uncommon_trap); - SET_ADDRESS(_extrs, OptoRuntime::handle_exception_C); - SET_ADDRESS(_extrs, OptoRuntime::new_instance_C); - SET_ADDRESS(_extrs, OptoRuntime::new_array_C); - SET_ADDRESS(_extrs, OptoRuntime::new_array_nozero_C); - SET_ADDRESS(_extrs, OptoRuntime::multianewarray2_C); - SET_ADDRESS(_extrs, OptoRuntime::multianewarray3_C); - SET_ADDRESS(_extrs, OptoRuntime::multianewarray4_C); - SET_ADDRESS(_extrs, OptoRuntime::multianewarray5_C); - SET_ADDRESS(_extrs, OptoRuntime::multianewarrayN_C); - SET_ADDRESS(_extrs, OptoRuntime::complete_monitor_locking_C); - SET_ADDRESS(_extrs, OptoRuntime::monitor_notify_C); - SET_ADDRESS(_extrs, OptoRuntime::monitor_notifyAll_C); - SET_ADDRESS(_extrs, OptoRuntime::rethrow_C); - SET_ADDRESS(_extrs, OptoRuntime::slow_arraycopy_C); - SET_ADDRESS(_extrs, OptoRuntime::register_finalizer_C); - SET_ADDRESS(_extrs, OptoRuntime::vthread_end_first_transition_C); - SET_ADDRESS(_extrs, OptoRuntime::vthread_start_final_transition_C); - SET_ADDRESS(_extrs, OptoRuntime::vthread_start_transition_C); - SET_ADDRESS(_extrs, OptoRuntime::vthread_end_transition_C); -#if defined(AARCH64) - SET_ADDRESS(_extrs, JavaThread::verify_cross_modify_fence_failure); -#endif // AARCH64 + ADD_EXTERNAL_ADDRESS(Deoptimization::uncommon_trap); + ADD_EXTERNAL_ADDRESS(OptoRuntime::handle_exception_C); + ADD_EXTERNAL_ADDRESS(OptoRuntime::new_instance_C); + ADD_EXTERNAL_ADDRESS(OptoRuntime::new_array_C); + ADD_EXTERNAL_ADDRESS(OptoRuntime::new_array_nozero_C); + ADD_EXTERNAL_ADDRESS(OptoRuntime::multianewarray2_C); + ADD_EXTERNAL_ADDRESS(OptoRuntime::multianewarray3_C); + ADD_EXTERNAL_ADDRESS(OptoRuntime::multianewarray4_C); + ADD_EXTERNAL_ADDRESS(OptoRuntime::multianewarray5_C); + ADD_EXTERNAL_ADDRESS(OptoRuntime::multianewarrayN_C); + ADD_EXTERNAL_ADDRESS(OptoRuntime::complete_monitor_locking_C); + ADD_EXTERNAL_ADDRESS(OptoRuntime::monitor_notify_C); + ADD_EXTERNAL_ADDRESS(OptoRuntime::monitor_notifyAll_C); + ADD_EXTERNAL_ADDRESS(OptoRuntime::rethrow_C); + ADD_EXTERNAL_ADDRESS(OptoRuntime::slow_arraycopy_C); + ADD_EXTERNAL_ADDRESS(OptoRuntime::register_finalizer_C); + ADD_EXTERNAL_ADDRESS(OptoRuntime::vthread_end_first_transition_C); + ADD_EXTERNAL_ADDRESS(OptoRuntime::vthread_start_final_transition_C); + ADD_EXTERNAL_ADDRESS(OptoRuntime::vthread_start_transition_C); + ADD_EXTERNAL_ADDRESS(OptoRuntime::vthread_end_transition_C); + // already added for +#if defined(AARCH64) && ! defined(PRODUCT) + ADD_EXTERNAL_ADDRESS(JavaThread::verify_cross_modify_fence_failure); +#endif // AARCH64 && !PRODUCT } #endif // COMPILER2 #if INCLUDE_G1GC - SET_ADDRESS(_extrs, G1BarrierSetRuntime::write_ref_field_pre_entry); + ADD_EXTERNAL_ADDRESS(G1BarrierSetRuntime::write_ref_field_pre_entry); + ADD_EXTERNAL_ADDRESS(G1BarrierSetRuntime::write_ref_array_pre_narrow_oop_entry); // used by arraycopy stubs + ADD_EXTERNAL_ADDRESS(G1BarrierSetRuntime::write_ref_array_pre_oop_entry); // used by arraycopy stubs + ADD_EXTERNAL_ADDRESS(G1BarrierSetRuntime::write_ref_array_post_entry); // used by arraycopy stubs + ADD_EXTERNAL_ADDRESS(BarrierSetNMethod::nmethod_stub_entry_barrier); // used by method_entry_barrier + #endif #if INCLUDE_SHENANDOAHGC - SET_ADDRESS(_extrs, ShenandoahRuntime::write_barrier_pre); - SET_ADDRESS(_extrs, ShenandoahRuntime::load_reference_barrier_phantom); - SET_ADDRESS(_extrs, ShenandoahRuntime::load_reference_barrier_phantom_narrow); + ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::write_barrier_pre); + ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::load_reference_barrier_strong); + ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::load_reference_barrier_strong_narrow); + ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::load_reference_barrier_weak); + ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::load_reference_barrier_weak_narrow); + ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::load_reference_barrier_phantom); + ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::load_reference_barrier_phantom_narrow); + ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::arraycopy_barrier_oop); + ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::arraycopy_barrier_narrow_oop); #endif #if INCLUDE_ZGC - SET_ADDRESS(_extrs, ZBarrierSetRuntime::load_barrier_on_oop_field_preloaded_addr()); - SET_ADDRESS(_extrs, ZBarrierSetRuntime::load_barrier_on_phantom_oop_field_preloaded_addr()); + ADD_EXTERNAL_ADDRESS(ZBarrierSetRuntime::load_barrier_on_oop_field_preloaded_addr()); + ADD_EXTERNAL_ADDRESS(ZBarrierSetRuntime::load_barrier_on_oop_field_preloaded_store_good_addr()); + ADD_EXTERNAL_ADDRESS(ZBarrierSetRuntime::load_barrier_on_weak_oop_field_preloaded_addr()); + ADD_EXTERNAL_ADDRESS(ZBarrierSetRuntime::load_barrier_on_phantom_oop_field_preloaded_addr()); + ADD_EXTERNAL_ADDRESS(ZBarrierSetRuntime::no_keepalive_load_barrier_on_weak_oop_field_preloaded_addr()); + ADD_EXTERNAL_ADDRESS(ZBarrierSetRuntime::no_keepalive_load_barrier_on_phantom_oop_field_preloaded_addr()); + ADD_EXTERNAL_ADDRESS(ZBarrierSetRuntime::store_barrier_on_oop_field_with_healing_addr()); + ADD_EXTERNAL_ADDRESS(ZBarrierSetRuntime::store_barrier_on_oop_field_without_healing_addr()); + ADD_EXTERNAL_ADDRESS(ZBarrierSetRuntime::no_keepalive_store_barrier_on_oop_field_without_healing_addr()); + ADD_EXTERNAL_ADDRESS(ZBarrierSetRuntime::store_barrier_on_native_oop_field_without_healing_addr()); + ADD_EXTERNAL_ADDRESS(ZBarrierSetRuntime::load_barrier_on_oop_array_addr()); + + ADD_EXTERNAL_ADDRESS(ZPointerVectorLoadBadMask); + ADD_EXTERNAL_ADDRESS(ZPointerVectorStoreBadMask); + ADD_EXTERNAL_ADDRESS(ZPointerVectorStoreGoodMask); #if defined(AMD64) - SET_ADDRESS(_extrs, &ZPointerLoadShift); + ADD_EXTERNAL_ADDRESS(&ZPointerLoadShift); + ADD_EXTERNAL_ADDRESS(&ZPointerLoadShiftTable); #endif #endif #ifndef ZERO #if defined(AMD64) || defined(AARCH64) || defined(RISCV64) - SET_ADDRESS(_extrs, MacroAssembler::debug64); -#endif + ADD_EXTERNAL_ADDRESS(MacroAssembler::debug64); +#endif // defined(AMD64) || defined(AARCH64) || defined(RISCV64) +#if defined(AMD64) + ADD_EXTERNAL_ADDRESS(warning); +#endif // defined(AMD64) #endif // ZERO // addresses of fields in AOT runtime constants area address* p = AOTRuntimeConstants::field_addresses_list(); while (*p != nullptr) { - SET_ADDRESS(_extrs, *p++); + address to_add = (address)*p++; + ADD_EXTERNAL_ADDRESS(to_add); } - _extrs_complete = true; - log_debug(aot, codecache, init)("External addresses recorded"); + log_debug(aot, codecache, init)("External addresses opened and recorded"); + // allocate storage for stub entries + _stubs_addr = NEW_C_HEAP_ARRAY(address, _stubs_max, mtCode); + log_debug(aot, codecache, init)("Stub addresses opened"); } -static bool initializing_early_stubs = false; - -void AOTCodeAddressTable::init_early_stubs() { - if (_complete || initializing_early_stubs) return; // Done already - initializing_early_stubs = true; - _stubs_addr = NEW_C_HEAP_ARRAY(address, _stubs_max, mtCode); - _stubs_length = 0; - SET_ADDRESS(_stubs, StubRoutines::forward_exception_entry()); +void AOTCodeAddressTable::init_extrs2() { + assert(initializing_extrs && !_extrs_complete, + "invalid sequence for init_extrs2"); { - // Required by C1 blobs -#if defined(AMD64) && !defined(ZERO) - SET_ADDRESS(_stubs, StubRoutines::x86::double_sign_flip()); - SET_ADDRESS(_stubs, StubRoutines::x86::d2l_fixup()); -#endif // AMD64 + ADD_EXTERNAL_ADDRESS(Continuation::prepare_thaw); // used by cont_thaw + ADD_EXTERNAL_ADDRESS(Continuation::thaw_entry()); // used by cont_thaw + ADD_EXTERNAL_ADDRESS(ContinuationEntry::thaw_call_pc_address()); // used by cont_preempt_stub } - - _early_stubs_complete = true; - log_info(aot, codecache, init)("Early stubs recorded"); + _extrs_complete = true; + initializing_extrs = false; + log_debug(aot, codecache, init)("External addresses recorded and closed"); } -static bool initializing_shared_blobs = false; - -void AOTCodeAddressTable::init_shared_blobs() { - if (_complete || initializing_shared_blobs) return; // Done already - initializing_shared_blobs = true; - address* blobs_addr = NEW_C_HEAP_ARRAY(address, _blobs_max, mtCode); - - // Divide _shared_blobs_addr array to chunks because they could be initialized in parrallel - _shared_blobs_addr = blobs_addr; - _C1_blobs_addr = _shared_blobs_addr + _shared_blobs_max; - - _shared_blobs_length = 0; - _C1_blobs_length = 0; - - // clear the address table - memset(blobs_addr, 0, sizeof(address)* _blobs_max); - - // Record addresses of generated code blobs - SET_ADDRESS(_shared_blobs, SharedRuntime::get_handle_wrong_method_stub()); - SET_ADDRESS(_shared_blobs, SharedRuntime::get_ic_miss_stub()); - SET_ADDRESS(_shared_blobs, SharedRuntime::deopt_blob()->unpack()); - SET_ADDRESS(_shared_blobs, SharedRuntime::deopt_blob()->unpack_with_exception()); - SET_ADDRESS(_shared_blobs, SharedRuntime::deopt_blob()->unpack_with_reexecution()); - SET_ADDRESS(_shared_blobs, SharedRuntime::deopt_blob()->unpack_with_exception_in_tls()); -#if INCLUDE_JVMCI - if (EnableJVMCI) { - SET_ADDRESS(_shared_blobs, SharedRuntime::deopt_blob()->uncommon_trap()); - SET_ADDRESS(_shared_blobs, SharedRuntime::deopt_blob()->implicit_exception_uncommon_trap()); +void AOTCodeAddressTable::add_external_addresses(GrowableArray
& addresses) { + assert(initializing_extrs && !_extrs_complete, + "invalid sequence for add_external_addresses"); + for (int i = 0; i < addresses.length(); i++) { + ADD_EXTERNAL_ADDRESS(addresses.at(i)); } -#endif - - _shared_blobs_complete = true; - log_debug(aot, codecache, init)("Early shared blobs recorded"); - _complete = true; + log_debug(aot, codecache, init)("Recorded %d additional external addresses", + addresses.length()); } -void AOTCodeAddressTable::init_early_c1() { -#ifdef COMPILER1 - // Runtime1 Blobs - StubId id = StubInfo::stub_base(StubGroup::C1); - // include forward_exception in range we publish - StubId limit = StubInfo::next(StubId::c1_forward_exception_id); - for (; id != limit; id = StubInfo::next(id)) { - if (Runtime1::blob_for(id) == nullptr) { - log_info(aot, codecache, init)("C1 blob %s is missing", Runtime1::name_for(id)); - continue; - } - if (Runtime1::entry_for(id) == nullptr) { - log_info(aot, codecache, init)("C1 blob %s is missing entry", Runtime1::name_for(id)); - continue; - } - address entry = Runtime1::entry_for(id); - SET_ADDRESS(_C1_blobs, entry); - } -#endif // COMPILER1 - assert(_C1_blobs_length <= _C1_blobs_max, "increase _C1_blobs_max to %d", _C1_blobs_length); - _early_c1_complete = true; +void AOTCodeAddressTable::add_stub_entry(EntryId entry_id, address a) { + assert(_extrs_complete || initializing_extrs, + "recording stub entry address before external addresses complete"); + assert(!(StubInfo::is_shared(StubInfo::stub(entry_id)) && _shared_stubs_complete), "too late to add shared entry"); + assert(!(StubInfo::is_stubgen(StubInfo::stub(entry_id)) && _stubgen_stubs_complete), "too late to add stubgen entry"); + assert(!(StubInfo::is_c1(StubInfo::stub(entry_id)) && _c1_stubs_complete), "too late to add c1 entry"); + assert(!(StubInfo::is_c2(StubInfo::stub(entry_id)) && _c2_stubs_complete), "too late to add c2 entry"); + log_debug(aot, stubs)("Recording address 0x%p for %s entry %s", a, StubInfo::name(StubInfo::stubgroup(entry_id)), StubInfo::name(entry_id)); + int idx = static_cast(entry_id); + hash_address(a, _stubs_base + idx); + _stubs_addr[idx] = a; } -#undef SET_ADDRESS +void AOTCodeAddressTable::set_shared_stubs_complete() { + assert(!_shared_stubs_complete, "repeated close for shared stubs!"); + _shared_stubs_complete = true; + log_debug(aot, codecache, init)("Shared stubs closed"); +} + +void AOTCodeAddressTable::set_c1_stubs_complete() { + assert(!_c1_stubs_complete, "repeated close for c1 stubs!"); + _c1_stubs_complete = true; + log_debug(aot, codecache, init)("C1 stubs closed"); +} + +void AOTCodeAddressTable::set_c2_stubs_complete() { + assert(!_c2_stubs_complete, "repeated close for c2 stubs!"); + _c2_stubs_complete = true; + log_debug(aot, codecache, init)("C2 stubs closed"); +} + +void AOTCodeAddressTable::set_stubgen_stubs_complete() { + assert(!_stubgen_stubs_complete, "repeated close for stubgen stubs!"); + _stubgen_stubs_complete = true; + log_debug(aot, codecache, init)("StubGen stubs closed"); +} #ifdef PRODUCT #define MAX_STR_COUNT 200 #else -#define MAX_STR_COUNT 500 +#define MAX_STR_COUNT 2000 #endif #define _c_str_max MAX_STR_COUNT static const int _c_str_base = _all_max; @@ -1540,6 +2201,10 @@ void AOTCodeCache::load_strings() { if (strings_count == 0) { return; } + if (strings_count > MAX_STR_COUNT) { + fatal("Invalid strings_count loaded from AOT Code Cache: %d > MAX_STR_COUNT [%d]", strings_count, MAX_STR_COUNT); + return; + } uint strings_offset = _load_header->strings_offset(); uint* string_lengths = (uint*)addr(strings_offset); strings_offset += (strings_count * sizeof(uint)); @@ -1550,12 +2215,12 @@ void AOTCodeCache::load_strings() { char* p = NEW_C_HEAP_ARRAY(char, strings_size+1, mtCode); memcpy(p, addr(strings_offset), strings_size); _C_strings_buf = p; - assert(strings_count <= MAX_STR_COUNT, "sanity"); for (uint i = 0; i < strings_count; i++) { _C_strings[i] = p; uint len = string_lengths[i]; _C_strings_s[i] = i; _C_strings_id[i] = i; + log_trace(aot, codecache, stringtable)("load_strings: _C_strings[%d] " INTPTR_FORMAT " '%s'", i, p2i(p), p); p += len; } assert((uint)(p - _C_strings_buf) <= strings_size, "(" INTPTR_FORMAT " - " INTPTR_FORMAT ") = %d > %d ", p2i(p), p2i(_C_strings_buf), (uint)(p - _C_strings_buf), strings_size); @@ -1575,6 +2240,7 @@ int AOTCodeCache::store_strings() { } for (int i = 0; i < _C_strings_used; i++) { const char* str = _C_strings[_C_strings_s[i]]; + log_trace(aot, codecache, stringtable)("store_strings: _C_strings[%d] " INTPTR_FORMAT " '%s'", i, p2i(str), str); uint len = (uint)strlen(str) + 1; length += len; assert(len < 1000, "big string: %s", str); @@ -1602,7 +2268,7 @@ const char* AOTCodeCache::add_C_string(const char* str) { } const char* AOTCodeAddressTable::add_C_string(const char* str) { - if (_extrs_complete) { + if (_extrs_complete || initializing_extrs) { // Check previous strings address for (int i = 0; i < _C_strings_count; i++) { if (_C_strings_in[i] == str) { @@ -1630,7 +2296,7 @@ const char* AOTCodeAddressTable::add_C_string(const char* str) { int AOTCodeAddressTable::id_for_C_string(address str) { if (str == nullptr) { - return -1; + return BAD_ADDRESS_ID; } MutexLocker ml(AOTCodeCStrings_lock, Mutex::_no_safepoint_check_flag); for (int i = 0; i < _C_strings_count; i++) { @@ -1640,6 +2306,7 @@ int AOTCodeAddressTable::id_for_C_string(address str) { assert(id < _C_strings_used, "%d >= %d", id , _C_strings_used); return id; // Found recorded } + log_trace(aot, codecache, stringtable)("id_for_C_string: _C_strings[%d ==> %d] " INTPTR_FORMAT " '%s'", i, _C_strings_used, p2i(str), str); // Not found in recorded, add new id = _C_strings_used++; _C_strings_s[id] = i; @@ -1647,7 +2314,7 @@ int AOTCodeAddressTable::id_for_C_string(address str) { return id; } } - return -1; + return BAD_ADDRESS_ID; } address AOTCodeAddressTable::address_for_C_string(int idx) { @@ -1665,7 +2332,7 @@ static int search_address(address addr, address* table, uint length) { } address AOTCodeAddressTable::address_for_id(int idx) { - assert(_extrs_complete, "AOT Code Cache VM runtime addresses table is not complete"); + assert(_extrs_complete || initializing_extrs, "AOT Code Cache VM runtime addresses table is not complete"); if (idx == -1) { return (address)-1; } @@ -1682,15 +2349,9 @@ address AOTCodeAddressTable::address_for_id(int idx) { if (/* id >= _extrs_base && */ id < _extrs_length) { return _extrs_addr[id - _extrs_base]; } - if (id >= _stubs_base && id < _stubs_base + _stubs_length) { + if (id >= _stubs_base && id < _c_str_base) { return _stubs_addr[id - _stubs_base]; } - if (id >= _shared_blobs_base && id < _shared_blobs_base + _shared_blobs_length) { - return _shared_blobs_addr[id - _shared_blobs_base]; - } - if (id >= _C1_blobs_base && id < _C1_blobs_base + _C1_blobs_length) { - return _C1_blobs_addr[id - _C1_blobs_base]; - } if (id >= _c_str_base && id < (_c_str_base + (uint)_C_strings_count)) { return address_for_C_string(id - _c_str_base); } @@ -1699,7 +2360,7 @@ address AOTCodeAddressTable::address_for_id(int idx) { } int AOTCodeAddressTable::id_for_address(address addr, RelocIterator reloc, CodeBlob* code_blob) { - assert(_extrs_complete, "AOT Code Cache VM runtime addresses table is not complete"); + assert(_extrs_complete || initializing_extrs, "AOT Code Cache VM runtime addresses table is not complete"); int id = -1; if (addr == (address)-1) { // Static call stub has jump to itself return id; @@ -1708,16 +2369,25 @@ int AOTCodeAddressTable::id_for_address(address addr, RelocIterator reloc, CodeB BarrierSet* bs = BarrierSet::barrier_set(); bool is_const_card_table_base = !UseG1GC && !UseShenandoahGC && bs->is_a(BarrierSet::CardTableBarrierSet); guarantee(!is_const_card_table_base || addr != ci_card_table_address_const(), "sanity"); - + // fast path for stubs and external addresses + if (_hash_table != nullptr) { + int *result = _hash_table->get(addr); + if (result != nullptr) { + id = *result; + log_trace(aot, codecache)("Address " INTPTR_FORMAT " retrieved from AOT Code Cache address hash table with index '%d'", + p2i(addr), id); + return id; + } + } // Seach for C string id = id_for_C_string(addr); - if (id >= 0) { + if (id != BAD_ADDRESS_ID) { return id + _c_str_base; } - if (StubRoutines::contains(addr)) { - // Search in stubs - id = search_address(addr, _stubs_addr, _stubs_length); - if (id < 0) { + if (StubRoutines::contains(addr) || CodeCache::find_blob(addr) != nullptr) { + // Search for a matching stub entry + id = search_address(addr, _stubs_addr, _stubs_max); + if (id == BAD_ADDRESS_ID) { StubCodeDesc* desc = StubCodeDesc::desc_for(addr); if (desc == nullptr) { desc = StubCodeDesc::desc_for(addr + frame::pc_return_offset); @@ -1728,51 +2398,39 @@ int AOTCodeAddressTable::id_for_address(address addr, RelocIterator reloc, CodeB return id + _stubs_base; } } else { - CodeBlob* cb = CodeCache::find_blob(addr); - if (cb != nullptr) { - // Search in code blobs - int id_base = _shared_blobs_base; - id = search_address(addr, _shared_blobs_addr, _blobs_max); - if (id < 0) { - assert(false, "Address " INTPTR_FORMAT " for Blob:%s is missing in AOT Code Cache addresses table", p2i(addr), cb->name()); + // Search in runtime functions + id = search_address(addr, _extrs_addr, _extrs_length); + if (id == BAD_ADDRESS_ID) { + ResourceMark rm; + const int buflen = 1024; + char* func_name = NEW_RESOURCE_ARRAY(char, buflen); + int offset = 0; + if (os::dll_address_to_function_name(addr, func_name, buflen, &offset)) { + if (offset > 0) { + // Could be address of C string + uint dist = (uint)pointer_delta(addr, (address)os::init, 1); + log_debug(aot, codecache)("Address " INTPTR_FORMAT " (offset %d) for runtime target '%s' is missing in AOT Code Cache addresses table", + p2i(addr), dist, (const char*)addr); + assert(dist > (uint)(_all_max + MAX_STR_COUNT), "change encoding of distance"); + return dist; + } +#ifdef ASSERT + reloc.print_current_on(tty); + code_blob->print_on(tty); + code_blob->print_code_on(tty); + assert(false, "Address " INTPTR_FORMAT " for runtime target '%s+%d' is missing in AOT Code Cache addresses table", p2i(addr), func_name, offset); +#endif } else { - return id_base + id; +#ifdef ASSERT + reloc.print_current_on(tty); + code_blob->print_on(tty); + code_blob->print_code_on(tty); + os::find(addr, tty); + assert(false, "Address " INTPTR_FORMAT " for /('%s') is missing in AOT Code Cache addresses table", p2i(addr), (const char*)addr); +#endif } } else { - // Search in runtime functions - id = search_address(addr, _extrs_addr, _extrs_length); - if (id < 0) { - ResourceMark rm; - const int buflen = 1024; - char* func_name = NEW_RESOURCE_ARRAY(char, buflen); - int offset = 0; - if (os::dll_address_to_function_name(addr, func_name, buflen, &offset)) { - if (offset > 0) { - // Could be address of C string - uint dist = (uint)pointer_delta(addr, (address)os::init, 1); - log_debug(aot, codecache)("Address " INTPTR_FORMAT " (offset %d) for runtime target '%s' is missing in AOT Code Cache addresses table", - p2i(addr), dist, (const char*)addr); - assert(dist > (uint)(_all_max + MAX_STR_COUNT), "change encoding of distance"); - return dist; - } -#ifdef ASSERT - reloc.print_current_on(tty); - code_blob->print_on(tty); - code_blob->print_code_on(tty); - assert(false, "Address " INTPTR_FORMAT " for runtime target '%s+%d' is missing in AOT Code Cache addresses table", p2i(addr), func_name, offset); -#endif - } else { -#ifdef ASSERT - reloc.print_current_on(tty); - code_blob->print_on(tty); - code_blob->print_code_on(tty); - os::find(addr, tty); - assert(false, "Address " INTPTR_FORMAT " for /('%s') is missing in AOT Code Cache addresses table", p2i(addr), (const char*)addr); -#endif - } - } else { - return _extrs_base + id; - } + return _extrs_base + id; } } return id; @@ -1839,3 +2497,162 @@ void AOTCodeCache::print_on(outputStream* st) { } } } + +// methods for managing entries in multi-stub blobs + + +AOTStubData::AOTStubData(BlobId blob_id) : + _blob_id(blob_id), + _cached_blob(nullptr), + _stub_cnt(0), + _ranges(nullptr), + _flags(0) { + assert(StubInfo::is_stubgen(blob_id), + "AOTStubData expects a multi-stub blob not %s", + StubInfo::name(blob_id)); + + // we cannot save or restore preuniversestubs because the cache + // cannot be accessed before initialising the universe + if (blob_id == BlobId::stubgen_preuniverse_id) { + // invalidate any attempt to use this + _flags = INVALID; + return; + } + if (AOTCodeCache::is_on()) { + _flags = OPEN; + // allow update of stub entry addresses + if (AOTCodeCache::is_using_stub()) { + // allow stub loading + _flags |= USING; + } + if (AOTCodeCache::is_dumping_stub()) { + // allow stub saving + _flags |= DUMPING; + } + // we need to track all the blob's entries + _stub_cnt = StubInfo::stub_count(_blob_id); + _ranges = NEW_C_HEAP_ARRAY(StubAddrRange, _stub_cnt, mtCode); + for (int i = 0; i < _stub_cnt; i++) { + _ranges[i].default_init(); + } + } +} + +bool AOTStubData::load_code_blob() { + assert(is_using(), "should not call"); + assert(!is_invalid() && _cached_blob == nullptr, "repeated init"); + _cached_blob = AOTCodeCache::load_code_blob(AOTCodeEntry::StubGenBlob, + _blob_id, + this); + if (_cached_blob == nullptr) { + set_invalid(); + return false; + } else { + return true; + } +} + +bool AOTStubData::store_code_blob(CodeBlob& new_blob, CodeBuffer *code_buffer) { + assert(is_dumping(), "should not call"); + assert(_cached_blob == nullptr, "should not be loading and storing!"); + if (!AOTCodeCache::store_code_blob(new_blob, + AOTCodeEntry::StubGenBlob, + _blob_id, this, code_buffer)) { + set_invalid(); + return false; + } else { + return true; + } +} + +address AOTStubData::load_archive_data(StubId stub_id, address& end, GrowableArray
* entries, GrowableArray
* extras) { + assert(StubInfo::blob(stub_id) == _blob_id, "sanity check"); + if (is_invalid()) { + return nullptr; + } + int idx = StubInfo::stubgen_offset_in_blob(_blob_id, stub_id); + assert(idx >= 0 && idx < _stub_cnt, "invalid index %d for stub count %d", idx, _stub_cnt); + // ensure we have a valid associated range + StubAddrRange &range = _ranges[idx]; + int base = range.start_index(); + if (base < 0) { +#ifdef DEBUG + // reset index so we can idenitfy which ones we failed to find + range.init_entry(-2, 0); +#endif + return nullptr; + } + int count = range.count(); + assert(base >= 0, "sanity"); + assert(count >= 2, "sanity"); + // first two saved addresses are start and end + address start = _address_array.at(base); + end = _address_array.at(base + 1); + assert(start != nullptr, "failed to load start address of stub %s", StubInfo::name(stub_id)); + assert(end != nullptr, "failed to load end address of stub %s", StubInfo::name(stub_id)); + assert(start < end, "start address %p should be less than end %p address for stub %s", start, end, StubInfo::name(stub_id)); + + int entry_count = StubInfo::entry_count(stub_id); + // the address count must at least include the stub start, end + // and secondary addresses + assert(count >= entry_count + 1, "stub %s requires %d saved addresses but only has %d", StubInfo::name(stub_id), entry_count + 1, count); + + // caller must retrieve secondary entries if and only if they exist + assert((entry_count == 1) == (entries == nullptr), "trying to retrieve wrong number of entries for stub %s", StubInfo::name(stub_id)); + int index = 2; + if (entries != nullptr) { + assert(entries->length() == 0, "non-empty array when retrieving entries for stub %s!", StubInfo::name(stub_id)); + while (index < entry_count + 1) { + address entry = _address_array.at(base + index++); + assert(entry == nullptr || (start < entry && entry < end), "entry address %p not in range (%p, %p) for stub %s", entry, start, end, StubInfo::name(stub_id)); + entries->append(entry); + } + } + // caller must retrieve extras if and only if they exist + assert((index < count) == (extras != nullptr), "trying to retrieve wrong number of extras for stub %s", StubInfo::name(stub_id)); + if (extras != nullptr) { + assert(extras->length() == 0, "non-empty array when retrieving extras for stub %s!", StubInfo::name(stub_id)); + while (index < count) { + address extra = _address_array.at(base + index++); + assert(extra == nullptr || (start <= extra && extra <= end), "extra address %p not in range (%p, %p) for stub %s", extra, start, end, StubInfo::name(stub_id)); + extras->append(extra); + } + } + + return start; +} + +void AOTStubData::store_archive_data(StubId stub_id, address start, address end, GrowableArray
* entries, GrowableArray
* extras) { + assert(StubInfo::blob(stub_id) == _blob_id, "sanity check"); + assert(start != nullptr, "start address cannot be null"); + assert(end != nullptr, "end address cannot be null"); + assert(start < end, "start address %p should be less than end %p address for stub %s", start, end, StubInfo::name(stub_id)); + int idx = StubInfo::stubgen_offset_in_blob(_blob_id, stub_id); + StubAddrRange& range = _ranges[idx]; + assert(range.start_index() == -1, "sanity"); + int base = _address_array.length(); + assert(base >= 0, "sanity"); + // first two saved addresses are start and end + _address_array.append(start); + _address_array.append(end); + // caller must save secondary entries if and only if they exist + assert((StubInfo::entry_count(stub_id) == 1) == (entries == nullptr), "trying to save wrong number of entries for stub %s", StubInfo::name(stub_id)); + if (entries != nullptr) { + assert(entries->length() == StubInfo::entry_count(stub_id) - 1, "incorrect entry count %d when saving entries for stub %s!", entries->length(), StubInfo::name(stub_id)); + for (int i = 0; i < entries->length(); i++) { + address entry = entries->at(i); + assert(entry == nullptr || (start < entry && entry < end), "entry address %p not in range (%p, %p) for stub %s", entry, start, end, StubInfo::name(stub_id)); + _address_array.append(entry); + } + } + // caller may wish to save extra addresses + if (extras != nullptr) { + for (int i = 0; i < extras->length(); i++) { + address extra = extras->at(i); + // handler range end may be end -- it gets restored as nullptr + assert(extra == nullptr || (start <= extra && extra <= end), "extra address %p not in range (%p, %p) for stub %s", extra, start, end, StubInfo::name(stub_id)); + _address_array.append(extra); + } + } + range.init_entry(base, _address_array.length() - base); +} diff --git a/src/hotspot/share/code/aotCodeCache.hpp b/src/hotspot/share/code/aotCodeCache.hpp index bd8721fea8c..2f314fe1eb6 100644 --- a/src/hotspot/share/code/aotCodeCache.hpp +++ b/src/hotspot/share/code/aotCodeCache.hpp @@ -25,8 +25,10 @@ #ifndef SHARE_CODE_AOTCODECACHE_HPP #define SHARE_CODE_AOTCODECACHE_HPP +#include "gc/shared/collectedHeap.hpp" #include "gc/shared/gc_globals.hpp" #include "runtime/stubInfo.hpp" +#include "utilities/hashTable.hpp" /* * AOT Code Cache collects code from Code Cache and corresponding metadata @@ -38,6 +40,7 @@ class CodeBuffer; class RelocIterator; class AOTCodeCache; +class AOTCodeReader; class AdapterBlob; class ExceptionBlob; class ImmutableOopMapSet; @@ -53,6 +56,7 @@ enum CompLevel : signed char; Fn(SharedBlob) \ Fn(C1Blob) \ Fn(C2Blob) \ + Fn(StubGenBlob) \ // Descriptor of AOT Code Cache's entry class AOTCodeEntry { @@ -114,48 +118,57 @@ public: address dumptime_content_start_addr() const { return _dumptime_content_start_addr; } static bool is_valid_entry_kind(Kind kind) { return kind > None && kind < Kind_count; } - static bool is_blob(Kind kind) { return kind == SharedBlob || kind == C1Blob || kind == C2Blob; } + static bool is_blob(Kind kind) { return kind == SharedBlob || kind == C1Blob || kind == C2Blob || kind == StubGenBlob; } + static bool is_single_stub_blob(Kind kind) { return kind == SharedBlob || kind == C1Blob || kind == C2Blob; } + static bool is_multi_stub_blob(Kind kind) { return kind == StubGenBlob; } static bool is_adapter(Kind kind) { return kind == Adapter; } }; +// we use a hash table to speed up translation of external addresses +// or stub addresses to their corresponding indexes when dumping stubs +// or nmethods to the AOT code cache. +class AOTCodeAddressHashTable : public HashTable< + address, + int, + 36137, // prime number + AnyObj::C_HEAP, + mtCode> {}; + // Addresses of stubs, blobs and runtime finctions called from compiled code. class AOTCodeAddressTable : public CHeapObj { private: address* _extrs_addr; address* _stubs_addr; - address* _shared_blobs_addr; - address* _C1_blobs_addr; uint _extrs_length; - uint _stubs_length; - uint _shared_blobs_length; - uint _C1_blobs_length; bool _extrs_complete; - bool _early_stubs_complete; - bool _shared_blobs_complete; - bool _early_c1_complete; - bool _complete; + bool _shared_stubs_complete; + bool _c1_stubs_complete; + bool _c2_stubs_complete; + bool _stubgen_stubs_complete; + AOTCodeAddressHashTable* _hash_table; + void hash_address(address addr, int idx); public: AOTCodeAddressTable() : _extrs_addr(nullptr), _stubs_addr(nullptr), - _shared_blobs_addr(nullptr), - _C1_blobs_addr(nullptr), _extrs_length(0), - _stubs_length(0), - _shared_blobs_length(0), - _C1_blobs_length(0), _extrs_complete(false), - _early_stubs_complete(false), - _shared_blobs_complete(false), - _early_c1_complete(false), - _complete(false) + _shared_stubs_complete(false), + _c1_stubs_complete(false), + _c2_stubs_complete(false), + _stubgen_stubs_complete(false), + _hash_table(nullptr) { } void init_extrs(); - void init_early_stubs(); - void init_shared_blobs(); - void init_early_c1(); + void init_extrs2(); + void add_stub_entry(EntryId entry_id, address entry); + void add_external_addresses(GrowableArray
& addresses) NOT_CDS_RETURN; + void set_shared_stubs_complete(); + void set_c1_stubs_complete(); + void set_c2_stubs_complete(); + void set_stubgen_stubs_complete(); const char* add_C_string(const char* str); int id_for_C_string(address str); address address_for_C_string(int idx); @@ -163,30 +176,217 @@ public: address address_for_id(int id); }; +// Auxiliary class used by AOTStubData to locate addresses owned by a +// stub in the _address_array. + +class StubAddrRange { +private: + // Index of the first address owned by a stub or -1 if none present + int _start_index; + // Total number of addresses owned by a stub, including in order: + // start address for stub code and first entry, (exclusive) end + // address for stub code, all secondary entry addresses, any + // auxiliary addresses + uint _naddr; + public: + StubAddrRange() : _start_index(-1), _naddr(0) {} + int start_index() { return _start_index; } + int count() { return _naddr; } + + void default_init() { + _start_index = -1; + _naddr = 0; + } + + void init_entry(int start_index, int naddr) { + _start_index = start_index; + _naddr = naddr; + } +}; + +// class used to save and restore details of stubs embedded in a +// multi-stub (StubGen) blob + +class AOTStubData : public StackObj { + friend class AOTCodeCache; + friend class AOTCodeReader; +private: + BlobId _blob_id; // must be a stubgen blob id + // whatever buffer blob was successfully loaded from the AOT cache + // following a call to load_code_blob or nullptr + CodeBlob *_cached_blob; + // Array of addresses owned by stubs. Each stub appends addresses to + // this array as a block, whether at the end of generation or at the + // end of restoration from the cache. The first two addresses in + // each block are the "start" and "end2 address of the stub. Any + // other visible addresses located within the range [start,end) + // follow, either extra entries, data addresses or SEGV-protected + // subrange start, end and handler addresses. In the special case + // that the SEGV handler address is the (external) common address + // handler the array will hold value nullptr. + GrowableArray
_address_array; + // count of how many stubs exist in the current blob (not all of + // which may actually be generated) + int _stub_cnt; + // array identifying range of entries in _address_array for each stub + // indexed by offset of stub in blob + StubAddrRange* _ranges; + + // flags indicating whether the AOT code cache is open and, if so, + // whether we are loading or storing stubs or have encountered any + // invalid stubs. + enum Flags { + OPEN = 1 << 0, // cache is open + USING = 1 << 1, // open and loading stubs + DUMPING = 1 << 2, // open and storing stubs + INVALID = 1 << 3, // found invalid stub when loading + }; + + uint32_t _flags; + + void set_invalid() { _flags |= INVALID; } + + StubAddrRange& get_range(int idx) const { return _ranges[idx]; } + GrowableArray
& address_array() { return _address_array; } + // accessor for entry/auxiliary addresses defaults to start entry +public: + AOTStubData(BlobId blob_id) NOT_CDS({}); + + ~AOTStubData() CDS_ONLY({FREE_C_HEAP_ARRAY(_ranges);}) NOT_CDS({}) + + bool is_open() CDS_ONLY({ return (_flags & OPEN) != 0; }) NOT_CDS_RETURN_(false); + bool is_using() CDS_ONLY({ return (_flags & USING) != 0; }) NOT_CDS_RETURN_(false); + bool is_dumping() CDS_ONLY({ return (_flags & DUMPING) != 0; }) NOT_CDS_RETURN_(false); + bool is_invalid() CDS_ONLY({ return (_flags & INVALID) != 0; }) NOT_CDS_RETURN_(false); + + BlobId blob_id() { return _blob_id; } + bool load_code_blob() NOT_CDS_RETURN_(true); + bool store_code_blob(CodeBlob& new_blob, CodeBuffer *code_buffer) NOT_CDS_RETURN_(true); + + address load_archive_data(StubId stub_id, address &end, GrowableArray
* entries = nullptr, GrowableArray
* extras = nullptr) NOT_CDS_RETURN_(nullptr); + void store_archive_data(StubId stub_id, address start, address end, GrowableArray
* entries = nullptr, GrowableArray
* extras = nullptr) NOT_CDS_RETURN; + + const AOTStubData* as_const() { return (const AOTStubData*)this; } +}; + +#define AOTCODECACHE_CONFIGS_GENERIC_DO(do_var, do_fun) \ + do_var(int, AllocateInstancePrefetchLines) /* stubs and nmethods */ \ + do_var(int, AllocatePrefetchDistance) /* stubs and nmethods */ \ + do_var(int, AllocatePrefetchLines) /* stubs and nmethods */ \ + do_var(int, AllocatePrefetchStepSize) /* stubs and nmethods */ \ + do_var(uint, CodeEntryAlignment) /* array copy stubs and nmethods */ \ + do_var(bool, UseCompressedOops) /* stubs and nmethods */ \ + do_var(bool, EnableContended) /* nmethods */ \ + do_var(intx, OptoLoopAlignment) /* array copy stubs and nmethods */ \ + do_var(bool, RestrictContended) /* nmethods */ \ + do_var(bool, UseAESCTRIntrinsics) \ + do_var(bool, UseAESIntrinsics) \ + do_var(bool, UseBASE64Intrinsics) \ + do_var(bool, UseChaCha20Intrinsics) \ + do_var(bool, UseCRC32CIntrinsics) \ + do_var(bool, UseCRC32Intrinsics) \ + do_var(bool, UseDilithiumIntrinsics) \ + do_var(bool, UseGHASHIntrinsics) \ + do_var(bool, UseKyberIntrinsics) \ + do_var(bool, UseMD5Intrinsics) \ + do_var(bool, UsePoly1305Intrinsics) \ + do_var(bool, UseSecondarySupersTable) \ + do_var(bool, UseSHA1Intrinsics) \ + do_var(bool, UseSHA256Intrinsics) \ + do_var(bool, UseSHA3Intrinsics) \ + do_var(bool, UseSHA512Intrinsics) \ + do_var(bool, UseVectorizedMismatchIntrinsic) \ + do_fun(int, CompressedKlassPointers_shift, CompressedKlassPointers::shift()) \ + do_fun(int, CompressedOops_shift, CompressedOops::shift()) \ + do_fun(bool, JavaAssertions_systemClassDefault, JavaAssertions::systemClassDefault()) \ + do_fun(bool, JavaAssertions_userClassDefault, JavaAssertions::userClassDefault()) \ + do_fun(CollectedHeap::Name, Universe_heap_kind, Universe::heap()->kind()) \ + // END + +#ifdef COMPILER2 +#define AOTCODECACHE_CONFIGS_COMPILER2_DO(do_var, do_fun) \ + do_var(intx, ArrayOperationPartialInlineSize) /* array copy stubs and nmethods */ \ + do_var(intx, MaxVectorSize) /* array copy/fill stubs */ \ + do_var(bool, UseMontgomeryMultiplyIntrinsic) \ + do_var(bool, UseMontgomerySquareIntrinsic) \ + do_var(bool, UseMulAddIntrinsic) \ + do_var(bool, UseMultiplyToLenIntrinsic) \ + do_var(bool, UseSquareToLenIntrinsic) \ + // END +#else +#define AOTCODECACHE_CONFIGS_COMPILER2_DO(do_var, do_fun) +#endif + +#if INCLUDE_JVMCI +#define AOTCODECACHE_CONFIGS_JVMCI_DO(do_var, do_fun) \ + do_var(bool, EnableJVMCI) /* adapters and nmethods */ \ + // END +#else +#define AOTCODECACHE_CONFIGS_JVMCI_DO(do_var, do_fun) +#endif + +#if defined(AARCH64) && !defined(ZERO) +#define AOTCODECACHE_CONFIGS_AARCH64_DO(do_var, do_fun) \ + do_var(intx, BlockZeroingLowLimit) /* array fill stubs */ \ + do_var(intx, PrefetchCopyIntervalInBytes) /* array copy stubs */ \ + do_var(int, SoftwarePrefetchHintDistance) /* array fill stubs */ \ + do_var(bool, UseBlockZeroing) \ + do_var(bool, UseLSE) /* stubs and nmethods */ \ + do_var(uint, UseSVE) /* stubs and nmethods */ \ + do_var(bool, UseSecondarySupersCache) \ + do_var(bool, UseSIMDForArrayEquals) /* array copy stubs and nmethods */ \ + do_var(bool, UseSIMDForBigIntegerShiftIntrinsics) \ + do_var(bool, UseSIMDForMemoryOps) /* array copy stubs and nmethods */ \ + do_var(bool, UseSIMDForSHA3Intrinsic) /* SHA3 stubs */ \ + do_var(bool, UseSimpleArrayEquals) \ + // END +#else +#define AOTCODECACHE_CONFIGS_AARCH64_DO(do_var, do_fun) +#endif + +#if defined(X86) && !defined(ZERO) +#define AOTCODECACHE_CONFIGS_X86_DO(do_var, do_fun) \ + do_var(int, AVX3Threshold) /* array copy stubs and nmethods */ \ + do_var(bool, EnableX86ECoreOpts) /* nmethods */ \ + do_var(int, UseAVX) /* array copy stubs and nmethods */ \ + do_var(bool, UseAPX) /* nmethods and stubs */ \ + do_var(bool, UseLibmIntrinsic) \ + do_var(bool, UseIntPolyIntrinsics) \ + // END +#else +#define AOTCODECACHE_CONFIGS_X86_DO(do_var, do_fun) +#endif + +#define AOTCODECACHE_CONFIGS_DO(do_var, do_fun) \ + AOTCODECACHE_CONFIGS_GENERIC_DO(do_var, do_fun) \ + AOTCODECACHE_CONFIGS_COMPILER2_DO(do_var, do_fun) \ + AOTCODECACHE_CONFIGS_JVMCI_DO(do_var, do_fun) \ + AOTCODECACHE_CONFIGS_AARCH64_DO(do_var, do_fun) \ + AOTCODECACHE_CONFIGS_X86_DO(do_var, do_fun) \ + // END + +#define AOTCODECACHE_DECLARE_VAR(type, name) type _saved_ ## name; +#define AOTCODECACHE_DECLARE_FUN(type, name, func) type _saved_ ## name; + class AOTCodeCache : public CHeapObj { // Classes used to describe AOT code cache. protected: class Config { - address _compressedOopBase; - uint _compressedOopShift; - uint _compressedKlassShift; - uint _contendedPaddingWidth; - uint _gc; - enum Flags { - none = 0, - debugVM = 1, - compressedOops = 2, - compressedClassPointers = 4, - useTLAB = 8, - systemClassAssertions = 16, - userClassAssertions = 32, - enableContendedPadding = 64, - restrictContendedPadding = 128 - }; - uint _flags; - uint _cpu_features_offset; // offset in the cache where cpu features are stored + AOTCODECACHE_CONFIGS_DO(AOTCODECACHE_DECLARE_VAR, AOTCODECACHE_DECLARE_FUN) + // Special configs that cannot be checked with macros + address _compressedOopBase; + +#if defined(X86) && !defined(ZERO) + bool _useUnalignedLoadStores; +#endif + +#if defined(AARCH64) && !defined(ZERO) + bool _avoidUnalignedAccesses; +#endif + + uint _cpu_features_offset; // offset in the cache where cpu features are stored public: void record(uint cpu_features_offset); bool verify_cpu_features(AOTCodeCache* cache) const; @@ -206,17 +406,18 @@ protected: uint _entries_offset; // offset of AOTCodeEntry array describing entries uint _adapters_count; uint _shared_blobs_count; + uint _stubgen_blobs_count; uint _C1_blobs_count; uint _C2_blobs_count; Config _config; // must be the last element as there is trailing data stored immediately after Config public: void init(uint cache_size, - uint strings_count, uint strings_offset, - uint entries_count, uint entries_offset, - uint adapters_count, uint shared_blobs_count, - uint C1_blobs_count, uint C2_blobs_count, - uint cpu_features_offset) { + uint strings_count, uint strings_offset, + uint entries_count, uint entries_offset, + uint adapters_count, uint shared_blobs_count, + uint stubgen_blobs_count, uint C1_blobs_count, + uint C2_blobs_count, uint cpu_features_offset) { _version = AOT_CODE_VERSION; _cache_size = cache_size; _strings_count = strings_count; @@ -225,6 +426,7 @@ protected: _entries_offset = entries_offset; _adapters_count = adapters_count; _shared_blobs_count = shared_blobs_count; + _stubgen_blobs_count = stubgen_blobs_count; _C1_blobs_count = C1_blobs_count; _C2_blobs_count = C2_blobs_count; _config.record(cpu_features_offset); @@ -237,6 +439,7 @@ protected: uint entries_count() const { return _entries_count; } uint entries_offset() const { return _entries_offset; } uint adapters_count() const { return _adapters_count; } + uint stubgen_blobs_count() const { return _stubgen_blobs_count; } uint shared_blobs_count() const { return _shared_blobs_count; } uint C1_blobs_count() const { return _C1_blobs_count; } uint C2_blobs_count() const { return _C2_blobs_count; } @@ -286,6 +489,7 @@ private: void clear_lookup_failed() { _lookup_failed = false; } bool lookup_failed() const { return _lookup_failed; } + void add_stub_entry(EntryId entry_id, address entry) NOT_CDS_RETURN; public: AOTCodeCache(bool is_dumping, bool is_using); @@ -301,9 +505,12 @@ public: void load_strings(); int store_strings(); - static void init_early_stubs_table() NOT_CDS_RETURN; - static void init_shared_blobs_table() NOT_CDS_RETURN; - static void init_early_c1_table() NOT_CDS_RETURN; + static void set_shared_stubs_complete() NOT_CDS_RETURN; + static void set_c1_stubs_complete() NOT_CDS_RETURN ; + static void set_c2_stubs_complete() NOT_CDS_RETURN; + static void set_stubgen_stubs_complete() NOT_CDS_RETURN; + + void add_stub_entries(StubId stub_id, address start, GrowableArray
*entries = nullptr, int offset = -1) NOT_CDS_RETURN; address address_for_C_string(int idx) const { return _table->address_for_C_string(idx); } address address_for_id(int id) const { return _table->address_for_id(id); } @@ -323,22 +530,41 @@ public: bool finish_write(); - bool write_relocations(CodeBlob& code_blob); + bool write_relocations(CodeBlob& code_blob, RelocIterator& iter); bool write_oop_map_set(CodeBlob& cb); + bool write_stub_data(CodeBlob& blob, AOTStubData *stub_data); #ifndef PRODUCT bool write_asm_remarks(CodeBlob& cb); bool write_dbg_strings(CodeBlob& cb); #endif // PRODUCT +private: + // internal private API to save and restore blobs + static bool store_code_blob(CodeBlob& blob, + AOTCodeEntry::Kind entry_kind, + uint id, + const char* name, + AOTStubData* stub_data, + CodeBuffer* code_buffer) NOT_CDS_RETURN_(false); + + static CodeBlob* load_code_blob(AOTCodeEntry::Kind kind, + uint id, + const char* name, + AOTStubData* stub_data) NOT_CDS_RETURN_(nullptr); + +public: // save and restore API for non-enumerable code blobs static bool store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind, - uint id, const char* name) NOT_CDS_RETURN_(false); + uint id, + const char* name) NOT_CDS_RETURN_(false); static CodeBlob* load_code_blob(AOTCodeEntry::Kind kind, uint id, const char* name) NOT_CDS_RETURN_(nullptr); // save and restore API for enumerable code blobs + + // API for single-stub blobs static bool store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind, BlobId id) NOT_CDS_RETURN_(false); @@ -346,6 +572,22 @@ public: static CodeBlob* load_code_blob(AOTCodeEntry::Kind kind, BlobId id) NOT_CDS_RETURN_(nullptr); + // API for multi-stub blobs -- for use by class StubGenerator. + + static bool store_code_blob(CodeBlob& blob, + AOTCodeEntry::Kind kind, + BlobId id, + AOTStubData* stub_data, + CodeBuffer *code_buffer) NOT_CDS_RETURN_(false); + + static CodeBlob* load_code_blob(AOTCodeEntry::Kind kind, + BlobId id, + AOTStubData* stub_data) NOT_CDS_RETURN_(nullptr); + + static void publish_external_addresses(GrowableArray
& addresses) NOT_CDS_RETURN; + // publish all entries for a code blob in code cache address table + static void publish_stub_addresses(CodeBlob &code_blob, BlobId id, AOTStubData *stub_data) NOT_CDS_RETURN; + static uint store_entries_cnt() { if (is_on_for_dump()) { return cache()->_store_entries_cnt; @@ -367,9 +609,14 @@ private: return true; } public: + // marker used where an address offset needs to be stored for later + // retrieval and the address turns out to be null + static const uint NULL_ADDRESS_MARKER = UINT_MAX; + static AOTCodeCache* cache() { assert(_passed_init2, "Too early to ask"); return _cache; } static void initialize() NOT_CDS_RETURN; static void init2() NOT_CDS_RETURN; + static void init3() NOT_CDS_RETURN; static void dump() NOT_CDS_RETURN; static bool is_on() CDS_ONLY({ return cache() != nullptr; }) NOT_CDS_RETURN_(false); static bool is_on_for_use() CDS_ONLY({ return is_on() && _cache->for_use(); }) NOT_CDS_RETURN_(false); @@ -390,7 +637,7 @@ public: // Concurent AOT code reader class AOTCodeReader { private: - const AOTCodeCache* _cache; + AOTCodeCache* _cache; const AOTCodeEntry* _entry; const char* _load_buffer; // Loaded cached code buffer uint _read_position; // Position in _load_buffer @@ -403,19 +650,33 @@ private: void clear_lookup_failed() { _lookup_failed = false; } bool lookup_failed() const { return _lookup_failed; } - AOTCodeEntry* aot_code_entry() { return (AOTCodeEntry*)_entry; } -public: - AOTCodeReader(AOTCodeCache* cache, AOTCodeEntry* entry); + // Values used by restore(code_blob). + // They should be set before calling it. + const char* _name; + address _reloc_data; + int _reloc_count; + ImmutableOopMapSet* _oop_maps; + AOTCodeEntry::Kind _entry_kind; + int _id; + AOTStubData* _stub_data; - CodeBlob* compile_code_blob(const char* name); + AOTCodeEntry* aot_code_entry() { return (AOTCodeEntry*)_entry; } ImmutableOopMapSet* read_oop_map_set(); + void read_stub_data(CodeBlob* code_blob, AOTStubData *stub_data); - void fix_relocations(CodeBlob* code_blob); + void fix_relocations(CodeBlob* code_blob, RelocIterator& iter); #ifndef PRODUCT void read_asm_remarks(AsmRemarks& asm_remarks); void read_dbg_strings(DbgStrings& dbg_strings); #endif // PRODUCT + +public: + AOTCodeReader(AOTCodeCache* cache, AOTCodeEntry* entry); + + CodeBlob* compile_code_blob(const char* name, AOTCodeEntry::Kind entry_kind, int id, AOTStubData* stub_data = nullptr); + + void restore(CodeBlob* code_blob); }; // code cache internal runtime constants area used by AOT code diff --git a/src/hotspot/share/code/codeBlob.cpp b/src/hotspot/share/code/codeBlob.cpp index fcc0b42a461..e0c286937d0 100644 --- a/src/hotspot/share/code/codeBlob.cpp +++ b/src/hotspot/share/code/codeBlob.cpp @@ -22,6 +22,7 @@ * */ +#include "code/aotCodeCache.hpp" #include "code/codeBlob.hpp" #include "code/codeCache.hpp" #include "code/relocInfo.hpp" @@ -39,6 +40,7 @@ #include "prims/forte.hpp" #include "prims/jvmtiExport.hpp" #include "runtime/handles.inline.hpp" +#include "runtime/icache.hpp" #include "runtime/interfaceSupport.inline.hpp" #include "runtime/javaFrameAnchor.hpp" #include "runtime/jniHandles.inline.hpp" @@ -77,8 +79,10 @@ const BufferBlob::Vptr BufferBlob::_vpntr; const RuntimeStub::Vptr RuntimeStub::_vpntr; const SingletonBlob::Vptr SingletonBlob::_vpntr; const DeoptimizationBlob::Vptr DeoptimizationBlob::_vpntr; +const SafepointBlob::Vptr SafepointBlob::_vpntr; #ifdef COMPILER2 const ExceptionBlob::Vptr ExceptionBlob::_vpntr; +const UncommonTrapBlob::Vptr UncommonTrapBlob::_vpntr; #endif // COMPILER2 const UpcallStub::Vptr UpcallStub::_vpntr; @@ -188,22 +192,6 @@ CodeBlob::CodeBlob(const char* name, CodeBlobKind kind, int size, uint16_t heade assert(_mutable_data == blob_end(), "sanity"); } -void CodeBlob::restore_mutable_data(address reloc_data) { - // Relocation data is now stored as part of the mutable data area; allocate it before copy relocations - 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, "codebuffer: no space for mutable data"); - } - } else { - _mutable_data = blob_end(); // default value - } - if (_relocation_size > 0) { - assert(_mutable_data_size > 0, "relocation is part of mutable data section"); - memcpy((address)relocation_begin(), reloc_data, relocation_size()); - } -} - void CodeBlob::purge() { assert(_mutable_data != nullptr, "should never be null"); if (_mutable_data != blob_end()) { @@ -240,6 +228,23 @@ void CodeBlob::print_code_on(outputStream* st) { Disassembler::decode(this, st); } +#if INCLUDE_CDS +void CodeBlob::restore_mutable_data(address reloc_data) { + // Relocation data is now stored as part of the mutable data area; allocate it before copy relocations + 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, "codebuffer: no space for mutable data"); + } + } else { + _mutable_data = blob_end(); // default value + } + if (_relocation_size > 0) { + assert(_mutable_data_size > 0, "relocation is part of mutable data section"); + memcpy((address)relocation_begin(), reloc_data, relocation_size()); + } +} + void CodeBlob::prepare_for_archiving_impl() { set_name(nullptr); _oop_maps = nullptr; @@ -269,24 +274,15 @@ void CodeBlob::post_restore() { vptr(_kind)->post_restore(this); } -CodeBlob* CodeBlob::restore(address code_cache_buffer, - const char* name, - address archived_reloc_data, - ImmutableOopMapSet* archived_oop_maps) +CodeBlob* CodeBlob::restore(address code_cache_buffer, AOTCodeReader* reader) { copy_to(code_cache_buffer); CodeBlob* code_blob = (CodeBlob*)code_cache_buffer; - code_blob->set_name(name); - code_blob->restore_mutable_data(archived_reloc_data); - code_blob->set_oop_maps(archived_oop_maps); + reader->restore(code_blob); return code_blob; } -CodeBlob* CodeBlob::create(CodeBlob* archived_blob, - const char* name, - address archived_reloc_data, - ImmutableOopMapSet* archived_oop_maps - ) +CodeBlob* CodeBlob::create(CodeBlob* archived_blob, AOTCodeReader* reader) { ThreadInVMfromUnknown __tiv; // get to VM state in case we block on CodeCache_lock @@ -298,10 +294,7 @@ CodeBlob* CodeBlob::create(CodeBlob* archived_blob, MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); address code_cache_buffer = (address)CodeCache::allocate(size, CodeBlobType::NonNMethod); if (code_cache_buffer != nullptr) { - blob = archived_blob->restore(code_cache_buffer, - name, - archived_reloc_data, - archived_oop_maps); + blob = archived_blob->restore(code_cache_buffer, reader); assert(blob != nullptr, "sanity check"); // Flush the code block @@ -315,6 +308,8 @@ CodeBlob* CodeBlob::create(CodeBlob* archived_blob, return blob; } +#endif // INCLUDE_CDS + //----------------------------------------------------------------------------------------- // Creates a RuntimeBlob from a CodeBuffer and copy code and relocation info. @@ -331,7 +326,15 @@ RuntimeBlob::RuntimeBlob( : CodeBlob(name, kind, cb, size, header_size, frame_complete, frame_size, oop_maps, caller_must_gc_arguments, align_up(cb->total_relocation_size(), oopSize)) { + if (code_size() == 0) { + // Nothing to copy + return; + } + cb->copy_code_and_locs_to(this); + + // Flush generated code + ICache::invalidate_range(code_begin(), code_size()); } void RuntimeBlob::free(RuntimeBlob* blob) { @@ -390,7 +393,7 @@ void RuntimeBlob::trace_new_stub(RuntimeBlob* stub, const char* name1, const cha // Implementation of BufferBlob BufferBlob::BufferBlob(const char* name, CodeBlobKind kind, int size, uint16_t header_size) -: RuntimeBlob(name, kind, size, header_size) + : RuntimeBlob(name, kind, size, header_size) {} BufferBlob* BufferBlob::create(const char* name, uint buffer_size) { @@ -625,8 +628,8 @@ DeoptimizationBlob::DeoptimizationBlob( int unpack_with_reexecution_offset, int frame_size ) -: SingletonBlob("DeoptimizationBlob", CodeBlobKind::Deoptimization, cb, - size, sizeof(DeoptimizationBlob), frame_size, oop_maps) + : SingletonBlob("DeoptimizationBlob", CodeBlobKind::Deoptimization, cb, + size, sizeof(DeoptimizationBlob), frame_size, oop_maps) { _unpack_offset = unpack_offset; _unpack_with_exception = unpack_with_exception_offset; @@ -675,8 +678,8 @@ UncommonTrapBlob::UncommonTrapBlob( OopMapSet* oop_maps, int frame_size ) -: SingletonBlob("UncommonTrapBlob", CodeBlobKind::UncommonTrap, cb, - size, sizeof(UncommonTrapBlob), frame_size, oop_maps) + : SingletonBlob("UncommonTrapBlob", CodeBlobKind::UncommonTrap, cb, + size, sizeof(UncommonTrapBlob), frame_size, oop_maps) {} @@ -707,8 +710,8 @@ ExceptionBlob::ExceptionBlob( OopMapSet* oop_maps, int frame_size ) -: SingletonBlob("ExceptionBlob", CodeBlobKind::Exception, cb, - size, sizeof(ExceptionBlob), frame_size, oop_maps) + : SingletonBlob("ExceptionBlob", CodeBlobKind::Exception, cb, + size, sizeof(ExceptionBlob), frame_size, oop_maps) {} @@ -741,8 +744,8 @@ SafepointBlob::SafepointBlob( OopMapSet* oop_maps, int frame_size ) -: SingletonBlob("SafepointBlob", CodeBlobKind::Safepoint, cb, - size, sizeof(SafepointBlob), frame_size, oop_maps) + : SingletonBlob(cb->name(), CodeBlobKind::Safepoint, cb, + size, sizeof(SafepointBlob), frame_size, oop_maps) {} @@ -759,7 +762,7 @@ SafepointBlob* SafepointBlob::create( blob = new (size) SafepointBlob(cb, size, oop_maps, frame_size); } - trace_new_stub(blob, "SafepointBlob"); + trace_new_stub(blob, "SafepointBlob - ", blob->name()); return blob; } @@ -899,7 +902,7 @@ void CodeBlob::dump_for_addr(address addr, outputStream* st, bool verbose) const } } if (is_nmethod()) { - nmethod* nm = (nmethod*)this; + nmethod* nm = as_nmethod(); ResourceMark rm; st->print(INTPTR_FORMAT " is at entry_point+%d in (nmethod*)" INTPTR_FORMAT, p2i(addr), (int)(addr - nm->entry_point()), p2i(nm)); @@ -935,7 +938,7 @@ void RuntimeStub::print_on_impl(outputStream* st) const { RuntimeBlob::print_on_impl(st); st->print("Runtime Stub (" INTPTR_FORMAT "): ", p2i(this)); st->print_cr("%s", name()); - Disassembler::decode((RuntimeBlob*)this, st); + Disassembler::decode((CodeBlob*)this, st); } void RuntimeStub::print_value_on_impl(outputStream* st) const { @@ -946,7 +949,7 @@ void SingletonBlob::print_on_impl(outputStream* st) const { ttyLocker ttyl; RuntimeBlob::print_on_impl(st); st->print_cr("%s", name()); - Disassembler::decode((RuntimeBlob*)this, st); + Disassembler::decode((CodeBlob*)this, st); } void SingletonBlob::print_value_on_impl(outputStream* st) const { @@ -964,7 +967,7 @@ void UpcallStub::print_on_impl(outputStream* st) const { oop recv = JNIHandles::resolve(_receiver); st->print("Receiver MH="); recv->print_on(st); - Disassembler::decode((RuntimeBlob*)this, st); + Disassembler::decode((CodeBlob*)this, st); } void UpcallStub::print_value_on_impl(outputStream* st) const { diff --git a/src/hotspot/share/code/codeBlob.hpp b/src/hotspot/share/code/codeBlob.hpp index 0469b6c71b1..709623de308 100644 --- a/src/hotspot/share/code/codeBlob.hpp +++ b/src/hotspot/share/code/codeBlob.hpp @@ -34,6 +34,7 @@ #include "utilities/align.hpp" #include "utilities/macros.hpp" +class AOTCodeReader; class ImmutableOopMap; class ImmutableOopMapSet; class JNIHandleBlock; @@ -44,9 +45,10 @@ class OopMapSet; enum class CodeBlobType { MethodNonProfiled = 0, // Execution level 1 and 4 (non-profiled) nmethods (including native nmethods) MethodProfiled = 1, // Execution level 2 and 3 (profiled) nmethods - NonNMethod = 2, // Non-nmethods like Buffers, Adapters and Runtime Stubs - All = 3, // All types (No code cache segmentation) - NumTypes = 4 // Number of CodeBlobTypes + MethodHot = 2, // Nmethods predicted to be always hot + NonNMethod = 3, // Non-nmethods like Buffers, Adapters and Runtime Stubs + All = 4, // All types (No code cache segmentation) + NumTypes = 5 // Number of CodeBlobTypes }; // CodeBlob - superclass for all entries in the CodeCache. @@ -97,7 +99,9 @@ enum class CodeBlobKind : u1 { class UpcallStub; // for as_upcall_stub() class RuntimeStub; // for as_runtime_stub() class JavaFrameAnchor; // for UpcallStub::jfa_for_frame +class BufferBlob; class AdapterBlob; +class SingletonBlob; class ExceptionBlob; class DeoptimizationBlob; class SafepointBlob; @@ -107,9 +111,6 @@ class CodeBlob { friend class VMStructs; friend class JVMCIVMStructs; -private: - void restore_mutable_data(address reloc_data); - protected: // order fields from large to small to minimize padding between fields ImmutableOopMapSet* _oop_maps; // OopMap for this CodeBlob @@ -169,8 +170,8 @@ protected: void operator delete(void* p) { } - void prepare_for_archiving_impl(); - void post_restore_impl(); + void prepare_for_archiving_impl() NOT_CDS_RETURN; + void post_restore_impl() NOT_CDS_RETURN; public: @@ -187,8 +188,19 @@ public: // Typing bool is_nmethod() const { return _kind == CodeBlobKind::Nmethod; } - bool is_buffer_blob() const { return _kind == CodeBlobKind::Buffer; } + // we may want to check for an actual buffer blob or subtype instance + bool is_buffer_blob(bool strict=true) const { + if (strict) { + return _kind == CodeBlobKind::Buffer; + } else { + return (_kind == CodeBlobKind::Buffer || + _kind == CodeBlobKind::Adapter || + _kind == CodeBlobKind::Vtable || + _kind == CodeBlobKind::MHAdapter); + } + } bool is_runtime_stub() const { return _kind == CodeBlobKind::RuntimeStub; } + // singleton blobs are never directly implemented bool is_deoptimization_stub() const { return _kind == CodeBlobKind::Deoptimization; } #ifdef COMPILER2 bool is_uncommon_trap_stub() const { return _kind == CodeBlobKind::UncommonTrap; } @@ -198,6 +210,12 @@ public: bool is_exception_stub() const { return false; } #endif bool is_safepoint_stub() const { return _kind == CodeBlobKind::Safepoint; } + bool is_singleton_blob() const { + return (is_deoptimization_stub() || + is_uncommon_trap_stub() || + is_exception_stub() || + is_safepoint_stub()); + } bool is_adapter_blob() const { return _kind == CodeBlobKind::Adapter; } bool is_vtable_blob() const { return _kind == CodeBlobKind::Vtable; } bool is_method_handles_adapter_blob() const { return _kind == CodeBlobKind::MHAdapter; } @@ -207,8 +225,12 @@ public: nmethod* as_nmethod_or_null() const { return is_nmethod() ? (nmethod*) this : nullptr; } nmethod* as_nmethod() const { assert(is_nmethod(), "must be nmethod"); return (nmethod*) this; } CodeBlob* as_codeblob() const { return (CodeBlob*) this; } + // we may want to force an actual buffer blob or subtype instance + BufferBlob* as_buffer_blob(bool strict = true) const { assert(is_buffer_blob(strict), "must be %sbuffer blob", (strict ? "strict " : "")); return (BufferBlob*) this; } AdapterBlob* as_adapter_blob() const { assert(is_adapter_blob(), "must be adapter blob"); return (AdapterBlob*) this; } ExceptionBlob* as_exception_blob() const { assert(is_exception_stub(), "must be exception stub"); return (ExceptionBlob*) this; } + // this will always return a subtype instance + SingletonBlob* as_singleton_blob() const { assert(is_singleton_blob(), "must be singleton blob"); return (SingletonBlob*) this; } DeoptimizationBlob* as_deoptimization_blob() const { assert(is_deoptimization_stub(), "must be deopt stub"); return (DeoptimizationBlob*) this; } SafepointBlob* as_safepoint_blob() const { assert(is_safepoint_stub(), "must be safepoint stub"); return (SafepointBlob*) this; } UpcallStub* as_upcall_stub() const { assert(is_upcall_stub(), "must be upcall stub"); return (UpcallStub*) this; } @@ -304,6 +326,9 @@ public: void use_strings(DbgStrings &strings) { _dbg_strings.share(strings); } #endif +#if INCLUDE_CDS + void restore_mutable_data(address reloc_data); + void copy_to(address buffer) { memcpy(buffer, this, this->size()); } @@ -314,11 +339,9 @@ public: // methods to restore a blob from AOT code cache into the CodeCache void post_restore(); - CodeBlob* restore(address code_cache_buffer, const char* name, address archived_reloc_data, ImmutableOopMapSet* archived_oop_maps); - static CodeBlob* create(CodeBlob* archived_blob, - const char* name, - address archived_reloc_data, - ImmutableOopMapSet* archived_oop_maps); + CodeBlob* restore(address code_cache_buffer, AOTCodeReader* reader); + static CodeBlob* create(CodeBlob* archived_blob, AOTCodeReader* reader); +#endif }; //---------------------------------------------------------------------------------------------------- @@ -388,10 +411,10 @@ class BufferBlob: public RuntimeBlob { class Vptr : public RuntimeBlob::Vptr { void print_on(const CodeBlob* instance, outputStream* st) const override { - ((const BufferBlob*)instance)->print_on_impl(st); + instance->as_buffer_blob(false)->print_on_impl(st); } void print_value_on(const CodeBlob* instance, outputStream* st) const override { - ((const BufferBlob*)instance)->print_value_on_impl(st); + instance->as_buffer_blob(false)->print_value_on_impl(st); } }; @@ -487,10 +510,17 @@ class RuntimeStub: public RuntimeBlob { address entry_point() const { return code_begin(); } + void post_restore_impl() { + trace_new_stub(this, "RuntimeStub - ", name()); + } + void print_on_impl(outputStream* st) const; void print_value_on_impl(outputStream* st) const; class Vptr : public RuntimeBlob::Vptr { + void post_restore(CodeBlob* instance) const override { + instance->as_runtime_stub()->post_restore_impl(); + } void print_on(const CodeBlob* instance, outputStream* st) const override { instance->as_runtime_stub()->print_on_impl(st); } @@ -532,10 +562,10 @@ class SingletonBlob: public RuntimeBlob { class Vptr : public RuntimeBlob::Vptr { void print_on(const CodeBlob* instance, outputStream* st) const override { - ((const SingletonBlob*)instance)->print_on_impl(st); + instance->as_singleton_blob()->print_on_impl(st); } void print_value_on(const CodeBlob* instance, outputStream* st) const override { - ((const SingletonBlob*)instance)->print_value_on_impl(st); + instance->as_singleton_blob()->print_value_on_impl(st); } }; @@ -574,7 +604,7 @@ class DeoptimizationBlob: public SingletonBlob { ); public: - static const int ENTRY_COUNT = 4 JVMTI_ONLY(+ 2); + static const int ENTRY_COUNT = 4 JVMCI_ONLY(+ 2); // Creation static DeoptimizationBlob* create( CodeBuffer* cb, @@ -606,20 +636,28 @@ class DeoptimizationBlob: public SingletonBlob { _uncommon_trap_offset = offset; assert(contains(code_begin() + _uncommon_trap_offset), "must be PC inside codeblob"); } - address uncommon_trap() const { return code_begin() + _uncommon_trap_offset; } + address uncommon_trap() const { return (EnableJVMCI ? code_begin() + _uncommon_trap_offset : nullptr); } void set_implicit_exception_uncommon_trap_offset(int offset) { _implicit_exception_uncommon_trap_offset = offset; assert(contains(code_begin() + _implicit_exception_uncommon_trap_offset), "must be PC inside codeblob"); } - address implicit_exception_uncommon_trap() const { return code_begin() + _implicit_exception_uncommon_trap_offset; } + address implicit_exception_uncommon_trap() const { return (EnableJVMCI ? code_begin() + _implicit_exception_uncommon_trap_offset : nullptr); } #endif // INCLUDE_JVMCI + void post_restore_impl() { + trace_new_stub(this, "DeoptimizationBlob"); + } + void print_value_on_impl(outputStream* st) const; class Vptr : public SingletonBlob::Vptr { + void post_restore(CodeBlob* instance) const override { + instance->as_deoptimization_blob()->post_restore_impl(); + } + void print_value_on(const CodeBlob* instance, outputStream* st) const override { - ((const DeoptimizationBlob*)instance)->print_value_on_impl(st); + instance->as_deoptimization_blob()->print_value_on_impl(st); } }; @@ -649,6 +687,16 @@ class UncommonTrapBlob: public SingletonBlob { OopMapSet* oop_maps, int frame_size ); + void post_restore_impl() { + trace_new_stub(this, "UncommonTrapBlob"); + } + class Vptr : public SingletonBlob::Vptr { + void post_restore(CodeBlob* instance) const override { + instance->as_uncommon_trap_blob()->post_restore_impl(); + } + }; + + static const Vptr _vpntr; }; @@ -679,7 +727,7 @@ class ExceptionBlob: public SingletonBlob { class Vptr : public SingletonBlob::Vptr { void post_restore(CodeBlob* instance) const override { - ((ExceptionBlob*)instance)->post_restore_impl(); + instance->as_exception_blob()->post_restore_impl(); } }; @@ -709,6 +757,17 @@ class SafepointBlob: public SingletonBlob { OopMapSet* oop_maps, int frame_size ); + + void post_restore_impl() { + trace_new_stub(this, "SafepointBlob - ", name()); + } + class Vptr : public SingletonBlob::Vptr { + void post_restore(CodeBlob* instance) const override { + instance->as_safepoint_blob()->post_restore_impl(); + } + }; + + static const Vptr _vpntr; }; //---------------------------------------------------------------------------------------------------- diff --git a/src/hotspot/share/code/codeCache.cpp b/src/hotspot/share/code/codeCache.cpp index 2a0256cc316..afed39847d2 100644 --- a/src/hotspot/share/code/codeCache.cpp +++ b/src/hotspot/share/code/codeCache.cpp @@ -201,6 +201,7 @@ void CodeCache::initialize_heaps() { CodeHeapInfo non_nmethod = {NonNMethodCodeHeapSize, FLAG_IS_CMDLINE(NonNMethodCodeHeapSize), true}; CodeHeapInfo profiled = {ProfiledCodeHeapSize, FLAG_IS_CMDLINE(ProfiledCodeHeapSize), true}; CodeHeapInfo non_profiled = {NonProfiledCodeHeapSize, FLAG_IS_CMDLINE(NonProfiledCodeHeapSize), true}; + CodeHeapInfo hot = {HotCodeHeapSize, FLAG_IS_CMDLINE(HotCodeHeapSize), true}; const bool cache_size_set = FLAG_IS_CMDLINE(ReservedCodeCacheSize); const size_t ps = page_size(false, 8); @@ -219,6 +220,12 @@ void CodeCache::initialize_heaps() { profiled.enabled = false; } + if (!heap_available(CodeBlobType::MethodHot)) { + hot.size = 0; + hot.set = true; + hot.enabled = false; + } + assert(heap_available(CodeBlobType::MethodNonProfiled), "MethodNonProfiled heap is always available for segmented code heap"); size_t compiler_buffer_size = 0; @@ -238,14 +245,36 @@ void CodeCache::initialize_heaps() { set_size_of_unset_code_heap(&non_profiled, cache_size, non_nmethod.size + profiled.size, min_size); } - if (!profiled.set && non_profiled.set) { - set_size_of_unset_code_heap(&profiled, cache_size, non_nmethod.size + non_profiled.size, min_size); + if (!profiled.set && non_profiled.set && hot.set) { + set_size_of_unset_code_heap(&profiled, cache_size, non_nmethod.size + non_profiled.size + hot.size, min_size); + } + + if (hot.enabled) { + if (!hot.set) { + assert(hot.size == 0, "must be calculated during heaps initialization"); + // An application usually has ~20% hot code which is mostly non-profiled code. + // We set the hot code heap size to 20% of the non-profiled code heap. + hot.size = MAX2(non_profiled.size / 5, min_size); + + if (non_profiled.set) { + err_msg msg("Must manually set HotCodeHeapSize when NonProfiledCodeHeapSize is set"); + vm_exit_during_initialization("Invalid code heap sizes", msg); + } + + non_profiled.size -= hot.size; + } + + if (hot.size > non_profiled.size) { + err_msg msg("Hot (%zuK) exceeds NonProfiled (%zuK).", + hot.size / K, non_profiled.size / K); + vm_exit_during_initialization("Invalid code heap sizes", msg); + } } // Compatibility. size_t non_nmethod_min_size = min_cache_size + compiler_buffer_size; - if (!non_nmethod.set && profiled.set && non_profiled.set) { - set_size_of_unset_code_heap(&non_nmethod, cache_size, profiled.size + non_profiled.size, non_nmethod_min_size); + if (!non_nmethod.set && profiled.set && non_profiled.set && hot.set) { + set_size_of_unset_code_heap(&non_nmethod, cache_size, profiled.size + non_profiled.size + hot.size, non_nmethod_min_size); } // Note: if large page support is enabled, min_size is at least the large @@ -253,8 +282,9 @@ void CodeCache::initialize_heaps() { non_nmethod.size = align_up(non_nmethod.size, min_size); profiled.size = align_up(profiled.size, min_size); non_profiled.size = align_up(non_profiled.size, min_size); + hot.size = align_up(hot.size, min_size); - size_t aligned_total = non_nmethod.size + profiled.size + non_profiled.size; + size_t aligned_total = non_nmethod.size + profiled.size + non_profiled.size + hot.size; if (!cache_size_set) { // If ReservedCodeCacheSize is explicitly set and exceeds CODE_CACHE_SIZE_LIMIT, // it is rejected by flag validation elsewhere. Here we only handle the case @@ -262,15 +292,15 @@ void CodeCache::initialize_heaps() { // sizes (after alignment) exceed the platform limit. if (aligned_total > CODE_CACHE_SIZE_LIMIT) { err_msg message("ReservedCodeCacheSize (%zuK), Max (%zuK)." - "Segments: NonNMethod (%zuK), NonProfiled (%zuK), Profiled (%zuK).", + "Segments: NonNMethod (%zuK), NonProfiled (%zuK), Profiled (%zuK), Hot (%zuK).", aligned_total/K, CODE_CACHE_SIZE_LIMIT/K, - non_nmethod.size/K, non_profiled.size/K, profiled.size/K); + non_nmethod.size/K, non_profiled.size/K, profiled.size/K, hot.size/K); vm_exit_during_initialization("Code cache size exceeds platform limit", message); } if (aligned_total != cache_size) { log_info(codecache)("ReservedCodeCache size %zuK changed to total segments size NonNMethod " - "%zuK NonProfiled %zuK Profiled %zuK = %zuK", - cache_size/K, non_nmethod.size/K, non_profiled.size/K, profiled.size/K, aligned_total/K); + "%zuK NonProfiled %zuK Profiled %zuK Hot %zuK = %zuK", + cache_size/K, non_nmethod.size/K, non_profiled.size/K, profiled.size/K, hot.size/K, aligned_total/K); // Adjust ReservedCodeCacheSize as necessary because it was not set explicitly cache_size = aligned_total; } @@ -295,19 +325,23 @@ void CodeCache::initialize_heaps() { } if (profiled.enabled && !profiled.set && profiled.size > min_size) { profiled.size -= min_size; + if (--delta == 0) break; + } + if (hot.enabled && !hot.set && hot.size > min_size) { + hot.size -= min_size; delta--; } if (delta == start_delta) { break; } } - aligned_total = non_nmethod.size + profiled.size + non_profiled.size; + aligned_total = non_nmethod.size + profiled.size + non_profiled.size + hot.size; } } log_debug(codecache)("Initializing code heaps ReservedCodeCache %zuK NonNMethod %zuK" - " NonProfiled %zuK Profiled %zuK", - cache_size/K, non_nmethod.size/K, non_profiled.size/K, profiled.size/K); + " NonProfiled %zuK Profiled %zuK Hot %zuK", + cache_size/K, non_nmethod.size/K, non_profiled.size/K, profiled.size/K, hot.size/K); // Validation // Check minimal required sizes @@ -318,6 +352,9 @@ void CodeCache::initialize_heaps() { if (non_profiled.enabled) { // non_profiled.enabled is always ON for segmented code heap, leave it checked for clarity check_min_size("non-profiled code heap", non_profiled.size, min_size); } + if (hot.enabled) { + check_min_size("hot code heap", hot.size, min_size); + } // ReservedCodeCacheSize was set explicitly, so report an error and abort if it doesn't match the segment sizes if (aligned_total != cache_size && cache_size_set) { @@ -328,6 +365,9 @@ void CodeCache::initialize_heaps() { if (non_profiled.enabled) { message.append(" + NonProfiledCodeHeapSize (%zuK)", non_profiled.size/K); } + if (hot.enabled) { + message.append(" + HotCodeHeapSize (%zuK)", hot.size/K); + } message.append(" = %zuK", aligned_total/K); message.append((aligned_total > cache_size) ? " is greater than " : " is less than "); message.append("ReservedCodeCacheSize (%zuK).", cache_size/K); @@ -348,6 +388,7 @@ void CodeCache::initialize_heaps() { FLAG_SET_ERGO(NonNMethodCodeHeapSize, non_nmethod.size); FLAG_SET_ERGO(ProfiledCodeHeapSize, profiled.size); FLAG_SET_ERGO(NonProfiledCodeHeapSize, non_profiled.size); + FLAG_SET_ERGO(HotCodeHeapSize, hot.size); FLAG_SET_ERGO(ReservedCodeCacheSize, cache_size); ReservedSpace rs = reserve_heap_memory(cache_size, ps); @@ -368,6 +409,13 @@ void CodeCache::initialize_heaps() { // Non-nmethods (stubs, adapters, ...) add_heap(non_method_space, "CodeHeap 'non-nmethods'", CodeBlobType::NonNMethod); + if (hot.enabled) { + ReservedSpace hot_space = rs.partition(offset, hot.size); + offset += hot.size; + // Nmethods known to be always hot. + add_heap(hot_space, "CodeHeap 'hot nmethods'", CodeBlobType::MethodHot); + } + if (non_profiled.enabled) { ReservedSpace non_profiled_space = rs.partition(offset, non_profiled.size); // Tier 1 and tier 4 (non-profiled) methods and native methods @@ -406,16 +454,25 @@ bool CodeCache::heap_available(CodeBlobType code_blob_type) { // Interpreter only: we don't need any method code heaps return (code_blob_type == CodeBlobType::NonNMethod); } else if (CompilerConfig::is_c1_profiling()) { - // Tiered compilation: use all code heaps + // Tiered compilation: use all code heaps including + // the hot code heap when it is present. + + if (COMPILER2_PRESENT(!HotCodeHeap &&) (code_blob_type == CodeBlobType::MethodHot)) { + return false; + } + return (code_blob_type < CodeBlobType::All); } else { // No TieredCompilation: we only need the non-nmethod and non-profiled code heap + // and the hot code heap if it is requested. return (code_blob_type == CodeBlobType::NonNMethod) || - (code_blob_type == CodeBlobType::MethodNonProfiled); + (code_blob_type == CodeBlobType::MethodNonProfiled) + COMPILER2_PRESENT(|| ((code_blob_type == CodeBlobType::MethodHot) && HotCodeHeap)); } } -const char* CodeCache::get_code_heap_flag_name(CodeBlobType code_blob_type) { +// Returns the name of the VM option to set the size of the corresponding CodeHeap +static const char* get_code_heap_flag_name(CodeBlobType code_blob_type) { switch(code_blob_type) { case CodeBlobType::NonNMethod: return "NonNMethodCodeHeapSize"; @@ -426,6 +483,9 @@ const char* CodeCache::get_code_heap_flag_name(CodeBlobType code_blob_type) { case CodeBlobType::MethodProfiled: return "ProfiledCodeHeapSize"; break; + case CodeBlobType::MethodHot: + return "HotCodeHeapSize"; + break; default: ShouldNotReachHere(); return nullptr; @@ -542,7 +602,7 @@ CodeBlob* CodeCache::allocate(uint size, CodeBlobType code_blob_type, bool handl // Get CodeHeap for the given CodeBlobType CodeHeap* heap = get_code_heap(code_blob_type); - assert(heap != nullptr, "heap is null"); + assert(heap != nullptr, "No heap for given code_blob_type (%d), heap is null", (int)code_blob_type); while (true) { cb = (CodeBlob*)heap->allocate(size); @@ -570,6 +630,9 @@ CodeBlob* CodeCache::allocate(uint size, CodeBlobType code_blob_type, bool handl type = CodeBlobType::MethodNonProfiled; } break; + case CodeBlobType::MethodHot: + type = CodeBlobType::MethodNonProfiled; + break; default: break; } @@ -1652,7 +1715,7 @@ void CodeCache::print_internals() { } } - FREE_C_HEAP_ARRAY(int, buckets); + FREE_C_HEAP_ARRAY(buckets); print_memory_overhead(); } diff --git a/src/hotspot/share/code/codeCache.hpp b/src/hotspot/share/code/codeCache.hpp index 349cc652bf4..6384cb397b8 100644 --- a/src/hotspot/share/code/codeCache.hpp +++ b/src/hotspot/share/code/codeCache.hpp @@ -118,10 +118,6 @@ class CodeCache : AllStatic { // Creates a new heap with the given name and size, containing CodeBlobs of the given type static void add_heap(ReservedSpace rs, const char* name, CodeBlobType code_blob_type); static CodeHeap* get_code_heap_containing(void* p); // Returns the CodeHeap containing the given pointer, or nullptr - static CodeHeap* get_code_heap(const void* cb); // Returns the CodeHeap for the given CodeBlob - static CodeHeap* get_code_heap(CodeBlobType code_blob_type); // Returns the CodeHeap for the given CodeBlobType - // Returns the name of the VM option to set the size of the corresponding CodeHeap - static const char* get_code_heap_flag_name(CodeBlobType code_blob_type); static ReservedSpace reserve_heap_memory(size_t size, size_t rs_ps); // Reserves one continuous chunk of memory for the CodeHeaps // Iteration @@ -145,6 +141,8 @@ class CodeCache : AllStatic { static int code_heap_compare(CodeHeap* const &lhs, CodeHeap* const &rhs); static void add_heap(CodeHeap* heap); + static CodeHeap* get_code_heap(const void* cb); // Returns the CodeHeap for the given CodeBlob + static CodeHeap* get_code_heap(CodeBlobType code_blob_type); // Returns the CodeHeap for the given CodeBlobType static const GrowableArray* heaps() { return _heaps; } static const GrowableArray* nmethod_heaps() { return _nmethod_heaps; } @@ -264,7 +262,7 @@ class CodeCache : AllStatic { } static bool code_blob_type_accepts_nmethod(CodeBlobType type) { - return type == CodeBlobType::All || type <= CodeBlobType::MethodProfiled; + return type == CodeBlobType::All || type <= CodeBlobType::MethodHot; } static bool code_blob_type_accepts_allocable(CodeBlobType type) { diff --git a/src/hotspot/share/code/compiledIC.cpp b/src/hotspot/share/code/compiledIC.cpp index 5f5c9711441..07d96d6cd44 100644 --- a/src/hotspot/share/code/compiledIC.cpp +++ b/src/hotspot/share/code/compiledIC.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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,11 +76,7 @@ CompiledICData::CompiledICData() // Inline cache callsite info is initialized once the first time it is resolved void CompiledICData::initialize(CallInfo* call_info, Klass* receiver_klass) { _speculated_method = call_info->selected_method(); - if (UseCompressedClassPointers) { - _speculated_klass = (uintptr_t)CompressedKlassPointers::encode_not_null(receiver_klass); - } else { - _speculated_klass = (uintptr_t)receiver_klass; - } + _speculated_klass = (uintptr_t)CompressedKlassPointers::encode_not_null(receiver_klass); if (call_info->call_kind() == CallInfo::itable_call) { assert(call_info->resolved_method() != nullptr, "virtual or interface method must be found"); _itable_defc_klass = call_info->resolved_method()->method_holder(); @@ -133,12 +129,7 @@ Klass* CompiledICData::speculated_klass() const { if (is_speculated_klass_unloaded()) { return nullptr; } - - if (UseCompressedClassPointers) { - return CompressedKlassPointers::decode_not_null((narrowKlass)_speculated_klass); - } else { - return (Klass*)_speculated_klass; - } + return CompressedKlassPointers::decode_not_null((narrowKlass)_speculated_klass); } //----------------------------------------------------------------------------- diff --git a/src/hotspot/share/code/exceptionHandlerTable.cpp b/src/hotspot/share/code/exceptionHandlerTable.cpp index a295d1271aa..f0ad9921cd0 100644 --- a/src/hotspot/share/code/exceptionHandlerTable.cpp +++ b/src/hotspot/share/code/exceptionHandlerTable.cpp @@ -32,7 +32,7 @@ void ExceptionHandlerTable::add_entry(HandlerTableEntry entry) { // not enough space => grow the table (amortized growth, double its size) guarantee(_size > 0, "no space allocated => cannot grow the table since it is part of nmethod"); int new_size = _size * 2; - _table = REALLOC_RESOURCE_ARRAY(HandlerTableEntry, _table, _size, new_size); + _table = REALLOC_RESOURCE_ARRAY(_table, _size, new_size); _size = new_size; } assert(_length < _size, "sanity check"); @@ -178,7 +178,7 @@ void ImplicitExceptionTable::append( uint exec_off, uint cont_off ) { if (_size == 0) _size = 4; _size *= 2; uint new_size_in_elements = _size*2; - _data = REALLOC_RESOURCE_ARRAY(uint, _data, old_size_in_elements, new_size_in_elements); + _data = REALLOC_RESOURCE_ARRAY(_data, old_size_in_elements, new_size_in_elements); } *(adr(l) ) = exec_off; *(adr(l)+1) = cont_off; diff --git a/src/hotspot/share/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp index 5a6ed8ab3ed..815c0c7b4b0 100644 --- a/src/hotspot/share/code/nmethod.cpp +++ b/src/hotspot/share/code/nmethod.cpp @@ -66,6 +66,10 @@ #include "runtime/flags/flagSetting.hpp" #include "runtime/frame.inline.hpp" #include "runtime/handles.inline.hpp" +#ifdef COMPILER2 +#include "runtime/hotCodeCollector.hpp" +#endif // COMPILER2 +#include "runtime/icache.hpp" #include "runtime/jniHandles.inline.hpp" #include "runtime/orderAccess.hpp" #include "runtime/os.hpp" @@ -1253,7 +1257,15 @@ void nmethod::post_init() { finalize_relocations(); + // Flush generated code + ICache::invalidate_range(code_begin(), code_size()); + Universe::heap()->register_nmethod(this); + +#ifdef COMPILER2 + HotCodeCollector::register_nmethod(this); +#endif // COMPILER2 + DEBUG_ONLY(Universe::heap()->verify_nmethod(this)); CodeCache::commit(this); @@ -1584,8 +1596,6 @@ nmethod* nmethod::relocate(CodeBlobType code_blob_type) { // 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); @@ -2036,7 +2046,7 @@ void nmethod::copy_values(GrowableArray* array) { // The code and relocations have already been initialized by the // CodeBlob constructor, so it is valid even at this early point to // iterate over relocations and patch the code. - fix_oop_relocations(nullptr, nullptr, /*initialize_immediates=*/ true); + fix_oop_relocations(/*initialize_immediates=*/ true); } void nmethod::copy_values(GrowableArray* array) { @@ -2048,24 +2058,42 @@ void nmethod::copy_values(GrowableArray* array) { } } -void nmethod::fix_oop_relocations(address begin, address end, bool initialize_immediates) { +bool nmethod::fix_oop_relocations(bool initialize_immediates) { // re-patch all oop-bearing instructions, just in case some oops moved - RelocIterator iter(this, begin, end); + RelocIterator iter(this); + bool modified_code = false; while (iter.next()) { if (iter.type() == relocInfo::oop_type) { oop_Relocation* reloc = iter.oop_reloc(); - if (initialize_immediates && reloc->oop_is_immediate()) { + if (!reloc->oop_is_immediate()) { + // Refresh the oop-related bits of this instruction. + reloc->set_value(reloc->value()); + modified_code = true; + } else if (initialize_immediates) { oop* dest = reloc->oop_addr(); jobject obj = *reinterpret_cast(dest); initialize_immediate_oop(dest, obj); } - // Refresh the oop-related bits of this instruction. - reloc->fix_oop_relocation(); } else if (iter.type() == relocInfo::metadata_type) { metadata_Relocation* reloc = iter.metadata_reloc(); reloc->fix_metadata_relocation(); + modified_code |= !reloc->metadata_is_immediate(); } } + return modified_code; +} + +void nmethod::fix_oop_relocations() { + ICacheInvalidationContext icic; + fix_oop_relocations(&icic); +} + +void nmethod::fix_oop_relocations(ICacheInvalidationContext* icic) { + assert(icic != nullptr, "must provide context to track if code was modified"); + bool modified_code = fix_oop_relocations(/*initialize_immediates=*/ false); + if (modified_code) { + icic->set_has_modified_code(); + } } static void install_post_call_nop_displacement(nmethod* nm, address pc) { @@ -2456,6 +2484,11 @@ void nmethod::purge(bool unregister_nmethod) { if (unregister_nmethod) { Universe::heap()->unregister_nmethod(this); } + +#ifdef COMPILER2 + HotCodeCollector::unregister_nmethod(this); +#endif // COMPILER2 + CodeCache::unregister_old_nmethod(this); JVMCI_ONLY( _metadata_size = 0; ) diff --git a/src/hotspot/share/code/nmethod.hpp b/src/hotspot/share/code/nmethod.hpp index 092da181f12..ea8c0e2ad5d 100644 --- a/src/hotspot/share/code/nmethod.hpp +++ b/src/hotspot/share/code/nmethod.hpp @@ -41,6 +41,7 @@ class Dependencies; class DirectiveSet; class DebugInformationRecorder; class ExceptionHandlerTable; +class ICacheInvalidationContext; class ImplicitExceptionTable; class JvmtiThreadState; class MetadataClosure; @@ -801,15 +802,15 @@ public: // Relocation support private: - void fix_oop_relocations(address begin, address end, bool initialize_immediates); + bool fix_oop_relocations(bool initialize_immediates); inline void initialize_immediate_oop(oop* dest, jobject handle); protected: address oops_reloc_begin() const; public: - void fix_oop_relocations(address begin, address end) { fix_oop_relocations(begin, end, false); } - void fix_oop_relocations() { fix_oop_relocations(nullptr, nullptr, false); } + void fix_oop_relocations(ICacheInvalidationContext* icic); + void fix_oop_relocations(); bool is_at_poll_return(address pc); bool is_at_poll_or_poll_return(address pc); diff --git a/src/hotspot/share/code/relocInfo.cpp b/src/hotspot/share/code/relocInfo.cpp index 2a6335e2118..73e4b6de7b4 100644 --- a/src/hotspot/share/code/relocInfo.cpp +++ b/src/hotspot/share/code/relocInfo.cpp @@ -590,15 +590,15 @@ oop oop_Relocation::oop_value() { return *oop_addr(); } - void oop_Relocation::fix_oop_relocation() { + // TODO: we need to add some assert here that ICache::invalidate_range is called in the code + // which uses this function. if (!oop_is_immediate()) { // get the oop from the pool, and re-insert it into the instruction: set_value(value()); } } - void oop_Relocation::verify_oop_relocation() { if (!oop_is_immediate()) { // get the oop from the pool, and re-insert it into the instruction: diff --git a/src/hotspot/share/compiler/cHeapStringHolder.cpp b/src/hotspot/share/compiler/cHeapStringHolder.cpp index 261658e04eb..5a9c124688e 100644 --- a/src/hotspot/share/compiler/cHeapStringHolder.cpp +++ b/src/hotspot/share/compiler/cHeapStringHolder.cpp @@ -36,7 +36,7 @@ void CHeapStringHolder::set(const char* string) { void CHeapStringHolder::clear() { if (_string != nullptr) { - FREE_C_HEAP_ARRAY(char, _string); + FREE_C_HEAP_ARRAY(_string); _string = nullptr; } } diff --git a/src/hotspot/share/compiler/compilationMemoryStatistic.cpp b/src/hotspot/share/compiler/compilationMemoryStatistic.cpp index 1951fd066fc..82bd4c160d2 100644 --- a/src/hotspot/share/compiler/compilationMemoryStatistic.cpp +++ b/src/hotspot/share/compiler/compilationMemoryStatistic.cpp @@ -487,7 +487,7 @@ public: void clean_details() { if (_detail_stats != nullptr) { - FREE_C_HEAP_ARRAY(Details, _detail_stats); + FREE_C_HEAP_ARRAY(_detail_stats); _detail_stats = nullptr; } } diff --git a/src/hotspot/share/compiler/compileLog.cpp b/src/hotspot/share/compiler/compileLog.cpp index d0ea80d6019..d0833fbe7fa 100644 --- a/src/hotspot/share/compiler/compileLog.cpp +++ b/src/hotspot/share/compiler/compileLog.cpp @@ -64,8 +64,8 @@ CompileLog::~CompileLog() { _out = nullptr; // Remove partial file after merging in CompileLog::finish_log_on_error unlink(_file); - FREE_C_HEAP_ARRAY(char, _identities); - FREE_C_HEAP_ARRAY(char, _file); + FREE_C_HEAP_ARRAY(_identities); + FREE_C_HEAP_ARRAY(_file); } @@ -96,7 +96,7 @@ int CompileLog::identify(ciBaseObject* obj) { if (id >= _identities_capacity) { int new_cap = _identities_capacity * 2; if (new_cap <= id) new_cap = id + 100; - _identities = REALLOC_C_HEAP_ARRAY(char, _identities, new_cap, mtCompiler); + _identities = REALLOC_C_HEAP_ARRAY(_identities, new_cap, mtCompiler); _identities_capacity = new_cap; } while (id >= _identities_limit) { diff --git a/src/hotspot/share/compiler/compilerDefinitions.cpp b/src/hotspot/share/compiler/compilerDefinitions.cpp index 0e4e211453b..cf7744cfe03 100644 --- a/src/hotspot/share/compiler/compilerDefinitions.cpp +++ b/src/hotspot/share/compiler/compilerDefinitions.cpp @@ -286,8 +286,38 @@ void CompilerConfig::set_compilation_policy_flags() { } } +#ifdef COMPILER2 + if (HotCodeHeap) { + if (FLAG_IS_DEFAULT(SegmentedCodeCache)) { + FLAG_SET_ERGO(SegmentedCodeCache, true); + } else if (!SegmentedCodeCache) { + vm_exit_during_initialization("HotCodeHeap requires SegmentedCodeCache enabled"); + } + + if (FLAG_IS_DEFAULT(NMethodRelocation)) { + FLAG_SET_ERGO(NMethodRelocation, true); + } else if (!NMethodRelocation) { + vm_exit_during_initialization("HotCodeHeap requires NMethodRelocation enabled"); + } + + if (!is_c2_enabled()) { + vm_exit_during_initialization("HotCodeHeap requires C2 enabled"); + } + + if (HotCodeMinSamplingMs > HotCodeMaxSamplingMs) { + vm_exit_during_initialization("HotCodeMinSamplingMs cannot be larger than HotCodeMaxSamplingMs"); + } + } else if (HotCodeHeapSize > 0) { + vm_exit_during_initialization("HotCodeHeapSize requires HotCodeHeap enabled"); + } +#else + if (HotCodeHeapSize > 0) { + vm_exit_during_initialization("HotCodeHeapSize requires C2 present"); + } +#endif // COMPILER2 + if (CompileThresholdScaling < 0) { - vm_exit_during_initialization("Negative value specified for CompileThresholdScaling", nullptr); + vm_exit_during_initialization("Negative value specified for CompileThresholdScaling"); } if (CompilationModeFlag::disable_intermediate()) { diff --git a/src/hotspot/share/compiler/compilerDirectives.cpp b/src/hotspot/share/compiler/compilerDirectives.cpp index 1cd8bd1b510..d0042d0e16c 100644 --- a/src/hotspot/share/compiler/compilerDirectives.cpp +++ b/src/hotspot/share/compiler/compilerDirectives.cpp @@ -256,7 +256,7 @@ ControlIntrinsicIter::ControlIntrinsicIter(ccstrlist option_value, bool disable_ } ControlIntrinsicIter::~ControlIntrinsicIter() { - FREE_C_HEAP_ARRAY(char, _list); + FREE_C_HEAP_ARRAY(_list); } // pre-increment diff --git a/src/hotspot/share/compiler/compilerDirectives.hpp b/src/hotspot/share/compiler/compilerDirectives.hpp index e4826b3056c..833d806f519 100644 --- a/src/hotspot/share/compiler/compilerDirectives.hpp +++ b/src/hotspot/share/compiler/compilerDirectives.hpp @@ -283,7 +283,7 @@ class ControlIntrinsicValidator { ~ControlIntrinsicValidator() { if (_bad != nullptr) { - FREE_C_HEAP_ARRAY(char, _bad); + FREE_C_HEAP_ARRAY(_bad); } } diff --git a/src/hotspot/share/compiler/directivesParser.cpp b/src/hotspot/share/compiler/directivesParser.cpp index a2da7c7e0e4..53a8325702e 100644 --- a/src/hotspot/share/compiler/directivesParser.cpp +++ b/src/hotspot/share/compiler/directivesParser.cpp @@ -198,7 +198,7 @@ bool DirectivesParser::push_key(const char* str, size_t len) { strncpy(s, str, len); s[len] = '\0'; error(KEY_ERROR, "No such key: '%s'.", s); - FREE_C_HEAP_ARRAY(char, s); + FREE_C_HEAP_ARRAY(s); return false; } @@ -370,7 +370,7 @@ bool DirectivesParser::set_option_flag(JSON_TYPE t, JSON_VAL* v, const key* opti #endif if (!valid) { - FREE_C_HEAP_ARRAY(char, s); + FREE_C_HEAP_ARRAY(s); return false; } (set->*test)((void *)&s); // Takes ownership. @@ -440,7 +440,7 @@ bool DirectivesParser::set_option(JSON_TYPE t, JSON_VAL* v) { assert (error_msg != nullptr, "Must have valid error message"); error(VALUE_ERROR, "Method pattern error: %s", error_msg); } - FREE_C_HEAP_ARRAY(char, s); + FREE_C_HEAP_ARRAY(s); } break; @@ -472,7 +472,7 @@ bool DirectivesParser::set_option(JSON_TYPE t, JSON_VAL* v) { error(VALUE_ERROR, "Method pattern error: %s", error_msg); } } - FREE_C_HEAP_ARRAY(char, s); + FREE_C_HEAP_ARRAY(s); } break; @@ -622,4 +622,3 @@ bool DirectivesParser::callback(JSON_TYPE t, JSON_VAL* v, uint rlimit) { } } } - diff --git a/src/hotspot/share/compiler/oopMap.cpp b/src/hotspot/share/compiler/oopMap.cpp index 87467d06400..c8d0c5d22ba 100644 --- a/src/hotspot/share/compiler/oopMap.cpp +++ b/src/hotspot/share/compiler/oopMap.cpp @@ -869,7 +869,7 @@ ImmutableOopMapSet* ImmutableOopMapSet::clone() const { } void ImmutableOopMapSet::operator delete(void* p) { - FREE_C_HEAP_ARRAY(unsigned char, p); + FREE_C_HEAP_ARRAY(p); } //------------------------------DerivedPointerTable--------------------------- diff --git a/src/hotspot/share/gc/epsilon/epsilonMonitoringSupport.cpp b/src/hotspot/share/gc/epsilon/epsilonMonitoringSupport.cpp index 213fc18b8ff..7ec4a0016db 100644 --- a/src/hotspot/share/gc/epsilon/epsilonMonitoringSupport.cpp +++ b/src/hotspot/share/gc/epsilon/epsilonMonitoringSupport.cpp @@ -70,7 +70,7 @@ public: } ~EpsilonSpaceCounters() { - FREE_C_HEAP_ARRAY(char, _name_space); + FREE_C_HEAP_ARRAY(_name_space); } inline void update_all(size_t capacity, size_t used) { diff --git a/src/hotspot/share/gc/g1/g1Allocator.cpp b/src/hotspot/share/gc/g1/g1Allocator.cpp index 78710084ee3..e9d1c13af7a 100644 --- a/src/hotspot/share/gc/g1/g1Allocator.cpp +++ b/src/hotspot/share/gc/g1/g1Allocator.cpp @@ -63,8 +63,8 @@ G1Allocator::~G1Allocator() { _mutator_alloc_regions[i].~MutatorAllocRegion(); _survivor_gc_alloc_regions[i].~SurvivorGCAllocRegion(); } - FREE_C_HEAP_ARRAY(MutatorAllocRegion, _mutator_alloc_regions); - FREE_C_HEAP_ARRAY(SurvivorGCAllocRegion, _survivor_gc_alloc_regions); + FREE_C_HEAP_ARRAY(_mutator_alloc_regions); + FREE_C_HEAP_ARRAY(_survivor_gc_alloc_regions); } #ifdef ASSERT @@ -315,7 +315,7 @@ G1PLABAllocator::PLABData::~PLABData() { for (uint node_index = 0; node_index < _num_alloc_buffers; node_index++) { delete _alloc_buffer[node_index]; } - FREE_C_HEAP_ARRAY(PLAB*, _alloc_buffer); + FREE_C_HEAP_ARRAY(_alloc_buffer); } void G1PLABAllocator::PLABData::initialize(uint num_alloc_buffers, size_t desired_plab_size, size_t tolerated_refills) { diff --git a/src/hotspot/share/gc/g1/g1Arguments.cpp b/src/hotspot/share/gc/g1/g1Arguments.cpp index 8bec6e7e86f..a0acd903b0f 100644 --- a/src/hotspot/share/gc/g1/g1Arguments.cpp +++ b/src/hotspot/share/gc/g1/g1Arguments.cpp @@ -98,7 +98,7 @@ void G1Arguments::initialize_verification_types() { parse_verification_type(token); token = strtok_r(nullptr, delimiter, &save_ptr); } - FREE_C_HEAP_ARRAY(char, type_list); + FREE_C_HEAP_ARRAY(type_list); } } @@ -148,8 +148,9 @@ void G1Arguments::initialize_card_set_configuration() { if (FLAG_IS_DEFAULT(G1RemSetArrayOfCardsEntries)) { uint max_cards_in_inline_ptr = G1CardSetConfiguration::max_cards_in_inline_ptr(G1HeapRegion::LogCardsPerRegion); + const JVMTypedFlagLimit* limit = JVMFlagLimit::get_range_at(FLAG_MEMBER_ENUM(G1RemSetArrayOfCardsEntries))->cast(); FLAG_SET_ERGO(G1RemSetArrayOfCardsEntries, MAX2(max_cards_in_inline_ptr * 2, - G1RemSetArrayOfCardsEntriesBase << region_size_log_mb)); + MIN2(G1RemSetArrayOfCardsEntriesBase << region_size_log_mb, limit->max()))); } // Howl card set container globals. diff --git a/src/hotspot/share/gc/g1/g1CardSet.cpp b/src/hotspot/share/gc/g1/g1CardSet.cpp index 60ad63e812c..f0db638a2fe 100644 --- a/src/hotspot/share/gc/g1/g1CardSet.cpp +++ b/src/hotspot/share/gc/g1/g1CardSet.cpp @@ -145,7 +145,7 @@ G1CardSetConfiguration::G1CardSetConfiguration(uint inline_ptr_bits_per_card, } G1CardSetConfiguration::~G1CardSetConfiguration() { - FREE_C_HEAP_ARRAY(size_t, _card_set_alloc_options); + FREE_C_HEAP_ARRAY(_card_set_alloc_options); } void G1CardSetConfiguration::init_card_set_alloc_options() { diff --git a/src/hotspot/share/gc/g1/g1CardSetMemory.cpp b/src/hotspot/share/gc/g1/g1CardSetMemory.cpp index 60602ef942b..95a32bae766 100644 --- a/src/hotspot/share/gc/g1/g1CardSetMemory.cpp +++ b/src/hotspot/share/gc/g1/g1CardSetMemory.cpp @@ -90,7 +90,7 @@ G1CardSetMemoryManager::~G1CardSetMemoryManager() { for (uint i = 0; i < num_mem_object_types(); i++) { _allocators[i].~G1CardSetAllocator(); } - FREE_C_HEAP_ARRAY(G1CardSetAllocator, _allocators); + FREE_C_HEAP_ARRAY(_allocators); } void G1CardSetMemoryManager::free(uint type, void* value) { diff --git a/src/hotspot/share/gc/g1/g1CardTableClaimTable.cpp b/src/hotspot/share/gc/g1/g1CardTableClaimTable.cpp index d8cabaa00a4..27b41ef165f 100644 --- a/src/hotspot/share/gc/g1/g1CardTableClaimTable.cpp +++ b/src/hotspot/share/gc/g1/g1CardTableClaimTable.cpp @@ -39,7 +39,7 @@ G1CardTableClaimTable::G1CardTableClaimTable(uint chunks_per_region) : } G1CardTableClaimTable::~G1CardTableClaimTable() { - FREE_C_HEAP_ARRAY(uint, _card_claims); + FREE_C_HEAP_ARRAY(_card_claims); } void G1CardTableClaimTable::initialize(uint max_reserved_regions) { diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index be9ecf19123..2709e6b3008 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -34,7 +34,7 @@ #include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1CollectionSet.hpp" #include "gc/g1/g1CollectionSetCandidates.hpp" -#include "gc/g1/g1CollectorState.hpp" +#include "gc/g1/g1CollectorState.inline.hpp" #include "gc/g1/g1ConcurrentMarkThread.inline.hpp" #include "gc/g1/g1ConcurrentRefine.hpp" #include "gc/g1/g1ConcurrentRefineThread.hpp" @@ -1652,6 +1652,7 @@ jint G1CollectedHeap::initialize() { } void G1CollectedHeap::stop() { + assert_not_at_safepoint(); // Stop all concurrent threads. We do this to make sure these threads // do not continue to execute and access resources (e.g. logging) // that are destroyed during shutdown. @@ -3218,7 +3219,7 @@ void G1CollectedHeap::retire_gc_alloc_region(G1HeapRegion* alloc_region, G1HeapRegionPrinter::retire(alloc_region); } -void G1CollectedHeap::mark_evac_failure_object(uint worker_id, const oop obj, size_t obj_size) const { +void G1CollectedHeap::mark_evac_failure_object(const oop obj) const { assert(!_cm->is_marked_in_bitmap(obj), "must be"); _cm->raw_mark_in_bitmap(obj); diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp index b5cb9167d92..3a47453819e 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp @@ -1275,7 +1275,7 @@ public: inline bool is_obj_dead_full(const oop obj) const; // Mark the live object that failed evacuation in the bitmap. - void mark_evac_failure_object(uint worker_id, oop obj, size_t obj_size) const; + void mark_evac_failure_object(oop obj) const; G1ConcurrentMark* concurrent_mark() const { return _cm; } diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp index 90e87607b87..bad9ac18eec 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp @@ -28,7 +28,6 @@ #include "gc/g1/g1CollectedHeap.hpp" #include "gc/g1/g1BarrierSet.hpp" -#include "gc/g1/g1CollectorState.hpp" #include "gc/g1/g1ConcurrentMark.inline.hpp" #include "gc/g1/g1EvacFailureRegions.hpp" #include "gc/g1/g1EvacStats.inline.hpp" diff --git a/src/hotspot/share/gc/g1/g1CollectionSet.cpp b/src/hotspot/share/gc/g1/g1CollectionSet.cpp index 978925d88cb..d2f9d30d87a 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSet.cpp +++ b/src/hotspot/share/gc/g1/g1CollectionSet.cpp @@ -26,7 +26,7 @@ #include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1CollectionSet.inline.hpp" #include "gc/g1/g1CollectionSetCandidates.inline.hpp" -#include "gc/g1/g1CollectorState.hpp" +#include "gc/g1/g1CollectorState.inline.hpp" #include "gc/g1/g1HeapRegion.inline.hpp" #include "gc/g1/g1HeapRegionRemSet.inline.hpp" #include "gc/g1/g1HeapRegionSet.hpp" @@ -72,7 +72,7 @@ G1CollectionSet::G1CollectionSet(G1CollectedHeap* g1h, G1Policy* policy) : } G1CollectionSet::~G1CollectionSet() { - FREE_C_HEAP_ARRAY(uint, _regions); + FREE_C_HEAP_ARRAY(_regions); abandon_all_candidates(); } @@ -766,7 +766,7 @@ public: } } ~G1VerifyYoungCSetIndicesClosure() { - FREE_C_HEAP_ARRAY(int, _heap_region_indices); + FREE_C_HEAP_ARRAY(_heap_region_indices); } virtual bool do_heap_region(G1HeapRegion* r) { diff --git a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp index 2113db1163b..3637d477229 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp +++ b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp @@ -214,7 +214,7 @@ G1CollectionSetCandidates::G1CollectionSetCandidates() : { } G1CollectionSetCandidates::~G1CollectionSetCandidates() { - FREE_C_HEAP_ARRAY(CandidateOrigin, _contains_map); + FREE_C_HEAP_ARRAY(_contains_map); _from_marking_groups.clear(); _retained_groups.clear(); } @@ -413,7 +413,7 @@ void G1CollectionSetCandidates::verify() { static_cast::type>(verify_map[i])); } - FREE_C_HEAP_ARRAY(CandidateOrigin, verify_map); + FREE_C_HEAP_ARRAY(verify_map); } #endif diff --git a/src/hotspot/share/gc/g1/g1CollectorState.cpp b/src/hotspot/share/gc/g1/g1CollectorState.cpp index 66292642603..76de9c65cc8 100644 --- a/src/hotspot/share/gc/g1/g1CollectorState.cpp +++ b/src/hotspot/share/gc/g1/g1CollectorState.cpp @@ -22,42 +22,32 @@ * */ -#include "gc/g1/g1CollectedHeap.inline.hpp" -#include "gc/g1/g1CollectorState.hpp" -#include "gc/g1/g1ConcurrentMarkThread.inline.hpp" +#include "gc/g1/g1CollectorState.inline.hpp" #include "runtime/safepoint.hpp" +#include "utilities/debug.hpp" G1CollectorState::Pause G1CollectorState::gc_pause_type(bool concurrent_operation_is_full_mark) const { assert(SafepointSynchronize::is_at_safepoint(), "must be"); switch (_phase) { case Phase::YoungNormal: return Pause::Normal; - case Phase::YoungLastYoung: return Pause::LastYoung; case Phase::YoungConcurrentStart: return concurrent_operation_is_full_mark ? Pause::ConcurrentStartFull : Pause::ConcurrentStartUndo; + case Phase::YoungPrepareMixed: return Pause::PrepareMixed; case Phase::Mixed: return Pause::Mixed; case Phase::FullGC: return Pause::Full; default: ShouldNotReachHere(); } } -bool G1CollectorState::is_in_concurrent_cycle() const { - G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); - return cm->is_in_concurrent_cycle(); +const char* G1CollectorState::to_string(Pause type) { + static const char* pause_strings[] = { "Normal", + "Concurrent Start", // Do not distinguish between the different + "Concurrent Start", // Concurrent Start pauses. + "Prepare Mixed", + "Cleanup", + "Remark", + "Mixed", + "Full" }; + return pause_strings[static_cast(type)]; } - -bool G1CollectorState::is_in_marking() const { - G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); - return cm->is_in_marking(); -} - -bool G1CollectorState::is_in_mark_or_rebuild() const { - G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); - return is_in_marking() || cm->is_in_rebuild_or_scrub(); -} - -bool G1CollectorState::is_in_reset_for_next_cycle() const { - G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); - return cm->is_in_reset_for_next_cycle(); -} - diff --git a/src/hotspot/share/gc/g1/g1CollectorState.hpp b/src/hotspot/share/gc/g1/g1CollectorState.hpp index fc59df7349d..42aaeab03b2 100644 --- a/src/hotspot/share/gc/g1/g1CollectorState.hpp +++ b/src/hotspot/share/gc/g1/g1CollectorState.hpp @@ -45,9 +45,9 @@ class G1CollectorState { // during that GC because we only decide whether we do this type of GC at the start // of the pause. YoungConcurrentStart, - // Indicates that we are about to start or in the last young gc in the Young-Only + // Indicates that we are about to start or in the prepare mixed gc in the Young-Only // phase before the Mixed phase. This GC is required to keep pause time requirements. - YoungLastYoung, + YoungPrepareMixed, // Doing extra old generation evacuation. Mixed, // The Full GC phase (that coincides with the Full GC pause). @@ -67,26 +67,25 @@ public: _initiate_conc_mark_if_possible(false) { } // Phase setters - void set_in_normal_young_gc() { _phase = Phase::YoungNormal; } - void set_in_space_reclamation_phase() { _phase = Phase::Mixed; } - void set_in_full_gc() { _phase = Phase::FullGC; } + inline void set_in_normal_young_gc(); + inline void set_in_space_reclamation_phase(); + inline void set_in_full_gc(); - // Pause setters - void set_in_young_gc_before_mixed() { _phase = Phase::YoungLastYoung; } - void set_in_concurrent_start_gc() { _phase = Phase::YoungConcurrentStart; _initiate_conc_mark_if_possible = false; } + inline void set_in_concurrent_start_gc(); + inline void set_in_prepare_mixed_gc(); - void set_initiate_conc_mark_if_possible(bool v) { _initiate_conc_mark_if_possible = v; } + inline void set_initiate_conc_mark_if_possible(bool v); // Phase getters - bool is_in_young_only_phase() const { return _phase == Phase::YoungNormal || _phase == Phase::YoungConcurrentStart || _phase == Phase::YoungLastYoung; } - bool is_in_mixed_phase() const { return _phase == Phase::Mixed; } + inline bool is_in_young_only_phase() const; + inline bool is_in_mixed_phase() const; // Specific pauses - bool is_in_young_gc_before_mixed() const { return _phase == Phase::YoungLastYoung; } - bool is_in_full_gc() const { return _phase == Phase::FullGC; } - bool is_in_concurrent_start_gc() const { return _phase == Phase::YoungConcurrentStart; } + inline bool is_in_concurrent_start_gc() const; + inline bool is_in_prepare_mixed_gc() const; + inline bool is_in_full_gc() const; - bool initiate_conc_mark_if_possible() const { return _initiate_conc_mark_if_possible; } + inline bool initiate_conc_mark_if_possible() const; bool is_in_concurrent_cycle() const; bool is_in_marking() const; @@ -95,9 +94,9 @@ public: enum class Pause : uint { Normal, - LastYoung, ConcurrentStartFull, ConcurrentStartUndo, + PrepareMixed, Cleanup, Remark, Mixed, @@ -107,50 +106,17 @@ public: // Calculate GC Pause Type from internal state. Pause gc_pause_type(bool concurrent_operation_is_full_mark) const; - static const char* to_string(Pause type) { - static const char* pause_strings[] = { "Normal", - "Prepare Mixed", - "Concurrent Start", // Do not distinguish between the different - "Concurrent Start", // Concurrent Start pauses. - "Cleanup", - "Remark", - "Mixed", - "Full" }; - return pause_strings[static_cast(type)]; - } + static const char* to_string(Pause type); - static void assert_is_young_pause(Pause type) { - assert(type != Pause::Full, "must be"); - assert(type != Pause::Remark, "must be"); - assert(type != Pause::Cleanup, "must be"); - } + // Pause kind queries + inline static void assert_is_young_pause(Pause type); - static bool is_young_only_pause(Pause type) { - assert_is_young_pause(type); - return type == Pause::ConcurrentStartUndo || - type == Pause::ConcurrentStartFull || - type == Pause::LastYoung || - type == Pause::Normal; - } + inline static bool is_young_only_pause(Pause type); + inline static bool is_concurrent_start_pause(Pause type); + inline static bool is_prepare_mixed_pause(Pause type); + inline static bool is_mixed_pause(Pause type); - static bool is_mixed_pause(Pause type) { - assert_is_young_pause(type); - return type == Pause::Mixed; - } - - static bool is_last_young_pause(Pause type) { - assert_is_young_pause(type); - return type == Pause::LastYoung; - } - - static bool is_concurrent_start_pause(Pause type) { - assert_is_young_pause(type); - return type == Pause::ConcurrentStartFull || type == Pause::ConcurrentStartUndo; - } - - static bool is_concurrent_cycle_pause(Pause type) { - return type == Pause::Cleanup || type == Pause::Remark; - } + inline static bool is_concurrent_cycle_pause(Pause type); }; ENUMERATOR_RANGE(G1CollectorState::Pause, G1CollectorState::Pause::Normal, G1CollectorState::Pause::Full) diff --git a/src/hotspot/share/gc/g1/g1CollectorState.inline.hpp b/src/hotspot/share/gc/g1/g1CollectorState.inline.hpp new file mode 100644 index 00000000000..0c6c9c879c3 --- /dev/null +++ b/src/hotspot/share/gc/g1/g1CollectorState.inline.hpp @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2026, 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. + * + */ + +#ifndef SHARE_GC_G1_G1COLLECTORSTATE_INLINE_HPP +#define SHARE_GC_G1_G1COLLECTORSTATE_INLINE_HPP + +#include "gc/g1/g1CollectorState.hpp" + +#include "gc/g1/g1CollectedHeap.hpp" +#include "gc/g1/g1ConcurrentMark.inline.hpp" + +inline void G1CollectorState::set_in_normal_young_gc() { + _phase = Phase::YoungNormal; +} +inline void G1CollectorState::set_in_space_reclamation_phase() { + _phase = Phase::Mixed; +} +inline void G1CollectorState::set_in_full_gc() { + _phase = Phase::FullGC; +} + +inline void G1CollectorState::set_in_concurrent_start_gc() { + _phase = Phase::YoungConcurrentStart; + _initiate_conc_mark_if_possible = false; +} +inline void G1CollectorState::set_in_prepare_mixed_gc() { + _phase = Phase::YoungPrepareMixed; +} + +inline void G1CollectorState::set_initiate_conc_mark_if_possible(bool v) { + _initiate_conc_mark_if_possible = v; +} + +inline bool G1CollectorState::is_in_young_only_phase() const { + return _phase == Phase::YoungNormal || + _phase == Phase::YoungConcurrentStart || + _phase == Phase::YoungPrepareMixed; +} +inline bool G1CollectorState::is_in_mixed_phase() const { + return _phase == Phase::Mixed; +} + +inline bool G1CollectorState::is_in_prepare_mixed_gc() const { + return _phase == Phase::YoungPrepareMixed; +} +inline bool G1CollectorState::is_in_full_gc() const { + return _phase == Phase::FullGC; +} +inline bool G1CollectorState::is_in_concurrent_start_gc() const { + return _phase == Phase::YoungConcurrentStart; +} + +inline bool G1CollectorState::initiate_conc_mark_if_possible() const { + return _initiate_conc_mark_if_possible; +} + +inline bool G1CollectorState::is_in_concurrent_cycle() const { + G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); + return cm->is_in_concurrent_cycle(); +} +inline bool G1CollectorState::is_in_marking() const { + G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); + return cm->is_in_marking(); +} +inline bool G1CollectorState::is_in_mark_or_rebuild() const { + G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); + return is_in_marking() || cm->is_in_rebuild_or_scrub(); +} +inline bool G1CollectorState::is_in_reset_for_next_cycle() const { + G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); + return cm->is_in_reset_for_next_cycle(); +} + +inline void G1CollectorState::assert_is_young_pause(Pause type) { + assert(type != Pause::Full, "must be"); + assert(type != Pause::Remark, "must be"); + assert(type != Pause::Cleanup, "must be"); +} + +inline bool G1CollectorState::is_young_only_pause(Pause type) { + assert_is_young_pause(type); + return type == Pause::ConcurrentStartUndo || + type == Pause::ConcurrentStartFull || + type == Pause::PrepareMixed || + type == Pause::Normal; +} + +inline bool G1CollectorState::is_mixed_pause(Pause type) { + assert_is_young_pause(type); + return type == Pause::Mixed; +} + +inline bool G1CollectorState::is_prepare_mixed_pause(Pause type) { + assert_is_young_pause(type); + return type == Pause::PrepareMixed; +} + +inline bool G1CollectorState::is_concurrent_start_pause(Pause type) { + assert_is_young_pause(type); + return type == Pause::ConcurrentStartFull || type == Pause::ConcurrentStartUndo; +} + +inline bool G1CollectorState::is_concurrent_cycle_pause(Pause type) { + return type == Pause::Cleanup || type == Pause::Remark; +} + +#endif // SHARE_GC_G1_G1COLLECTORSTATE_INLINE_HPP diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp index 8fd355615d0..d5e3b9cc69d 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp @@ -30,7 +30,7 @@ #include "gc/g1/g1CardSetMemory.hpp" #include "gc/g1/g1CardTableClaimTable.inline.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" -#include "gc/g1/g1CollectorState.hpp" +#include "gc/g1/g1CollectorState.inline.hpp" #include "gc/g1/g1ConcurrentMark.inline.hpp" #include "gc/g1/g1ConcurrentMarkRemarkTasks.hpp" #include "gc/g1/g1ConcurrentMarkThread.inline.hpp" @@ -249,7 +249,7 @@ G1CMMarkStack::ChunkAllocator::~ChunkAllocator() { } } - FREE_C_HEAP_ARRAY(TaskQueueEntryChunk*, _buckets); + FREE_C_HEAP_ARRAY(_buckets); } bool G1CMMarkStack::ChunkAllocator::reserve(size_t new_capacity) { @@ -372,63 +372,56 @@ void G1CMMarkStack::set_empty() { G1CMRootMemRegions::G1CMRootMemRegions(uint const max_regions) : _root_regions(MemRegion::create_array(max_regions, mtGC)), _max_regions(max_regions), - _num_root_regions(0), - _claimed_root_regions(0), - _scan_in_progress(false), - _should_abort(false) { } + _num_regions(0), + _num_claimed_regions(0) { } G1CMRootMemRegions::~G1CMRootMemRegions() { MemRegion::destroy_array(_root_regions, _max_regions); } void G1CMRootMemRegions::reset() { - _num_root_regions.store_relaxed(0); + assert_at_safepoint(); + assert(G1CollectedHeap::heap()->collector_state()->is_in_concurrent_start_gc(), "must be"); + + _num_regions.store_relaxed(0); + _num_claimed_regions.store_relaxed(0); } void G1CMRootMemRegions::add(HeapWord* start, HeapWord* end) { assert_at_safepoint(); - size_t idx = _num_root_regions.fetch_then_add(1u); - assert(idx < _max_regions, "Trying to add more root MemRegions than there is space %zu", _max_regions); + uint idx = _num_regions.fetch_then_add(1u); + assert(idx < _max_regions, "Trying to add more root MemRegions than there is space %u", _max_regions); assert(start != nullptr && end != nullptr && start <= end, "Start (" PTR_FORMAT ") should be less or equal to " "end (" PTR_FORMAT ")", p2i(start), p2i(end)); _root_regions[idx].set_start(start); _root_regions[idx].set_end(end); } -void G1CMRootMemRegions::prepare_for_scan() { - assert(!scan_in_progress(), "pre-condition"); - - _scan_in_progress.store_relaxed(num_root_regions() > 0); - - _claimed_root_regions.store_relaxed(0); - _should_abort.store_relaxed(false); -} - const MemRegion* G1CMRootMemRegions::claim_next() { - if (_should_abort.load_relaxed()) { - // If someone has set the should_abort flag, we return null to - // force the caller to bail out of their loop. + uint local_num_regions = num_regions(); + if (num_claimed_regions() >= local_num_regions) { return nullptr; } - uint local_num_root_regions = num_root_regions(); - if (_claimed_root_regions.load_relaxed() >= local_num_root_regions) { - return nullptr; - } - - size_t claimed_index = _claimed_root_regions.fetch_then_add(1u); - if (claimed_index < local_num_root_regions) { + uint claimed_index = _num_claimed_regions.fetch_then_add(1u); + if (claimed_index < local_num_regions) { return &_root_regions[claimed_index]; } return nullptr; } -uint G1CMRootMemRegions::num_root_regions() const { - return (uint)_num_root_regions.load_relaxed(); +bool G1CMRootMemRegions::work_completed() const { + return num_remaining_regions() == 0; +} + +uint G1CMRootMemRegions::num_remaining_regions() const { + uint total = num_regions(); + uint claimed = num_claimed_regions(); + return (total > claimed) ? total - claimed : 0; } bool G1CMRootMemRegions::contains(const MemRegion mr) const { - uint local_num_root_regions = num_root_regions(); + uint local_num_root_regions = num_regions(); for (uint i = 0; i < local_num_root_regions; i++) { if (_root_regions[i].equals(mr)) { return true; @@ -437,42 +430,6 @@ bool G1CMRootMemRegions::contains(const MemRegion mr) const { return false; } -void G1CMRootMemRegions::notify_scan_done() { - MutexLocker x(G1RootRegionScan_lock, Mutex::_no_safepoint_check_flag); - _scan_in_progress.store_relaxed(false); - G1RootRegionScan_lock->notify_all(); -} - -void G1CMRootMemRegions::cancel_scan() { - notify_scan_done(); -} - -void G1CMRootMemRegions::scan_finished() { - assert(scan_in_progress(), "pre-condition"); - - if (!_should_abort.load_relaxed()) { - assert(_claimed_root_regions.load_relaxed() >= num_root_regions(), - "we should have claimed all root regions, claimed %zu, length = %u", - _claimed_root_regions.load_relaxed(), num_root_regions()); - } - - notify_scan_done(); -} - -bool G1CMRootMemRegions::wait_until_scan_finished() { - if (!scan_in_progress()) { - return false; - } - - { - MonitorLocker ml(G1RootRegionScan_lock, Mutex::_no_safepoint_check_flag); - while (scan_in_progress()) { - ml.wait(); - } - } - return true; -} - G1ConcurrentMark::G1ConcurrentMark(G1CollectedHeap* g1h, G1RegionToSpaceMapper* bitmap_storage) : _cm_thread(nullptr), @@ -483,6 +440,7 @@ G1ConcurrentMark::G1ConcurrentMark(G1CollectedHeap* g1h, _heap(_g1h->reserved()), _root_regions(_g1h->max_num_regions()), + _root_region_scan_aborted(false), _global_mark_stack(), @@ -614,6 +572,7 @@ void G1ConcurrentMark::reset() { _region_mark_stats[i].clear(); } + _root_region_scan_aborted.store_relaxed(false); _root_regions.reset(); } @@ -717,9 +676,9 @@ void G1ConcurrentMark::reset_at_marking_complete() { } G1ConcurrentMark::~G1ConcurrentMark() { - FREE_C_HEAP_ARRAY(Atomic, _top_at_mark_starts); - FREE_C_HEAP_ARRAY(Atomic, _top_at_rebuild_starts); - FREE_C_HEAP_ARRAY(G1RegionMarkStats, _region_mark_stats); + FREE_C_HEAP_ARRAY(_top_at_mark_starts); + FREE_C_HEAP_ARRAY(_top_at_rebuild_starts); + FREE_C_HEAP_ARRAY(_region_mark_stats); // The G1ConcurrentMark instance is never freed. ShouldNotReachHere(); } @@ -970,8 +929,6 @@ void G1ConcurrentMark::start_full_concurrent_cycle() { satb_mq_set.set_active_all_threads(true, /* new active value */ false /* expected_active */); - _root_regions.prepare_for_scan(); - // update_g1_committed() will be called at the end of an evac pause // when marking is on. So, it's also called at the end of the // concurrent start pause to update the heap end, if the heap expands @@ -982,7 +939,11 @@ void G1ConcurrentMark::start_full_concurrent_cycle() { } void G1ConcurrentMark::start_undo_concurrent_cycle() { - root_regions()->cancel_scan(); + assert_at_safepoint_on_vm_thread(); + // At this time this GC is not a concurrent start gc any more, can only check for young only gc/phase. + assert(_g1h->collector_state()->is_in_young_only_phase(), "must be"); + + abort_root_region_scan_at_safepoint(); // Signal the thread to start work. cm_thread()->start_undo_cycle(); @@ -1094,6 +1055,16 @@ uint G1ConcurrentMark::calc_active_marking_workers() { return result; } +bool G1ConcurrentMark::has_root_region_scan_aborted() const { + return _root_region_scan_aborted.load_relaxed(); +} + +#ifndef PRODUCT +void G1ConcurrentMark::assert_root_region_scan_completed_or_aborted() { + assert(root_regions()->work_completed() || has_root_region_scan_aborted(), "must be"); +} +#endif + void G1ConcurrentMark::scan_root_region(const MemRegion* region, uint worker_id) { #ifdef ASSERT HeapWord* last = region->last(); @@ -1120,45 +1091,87 @@ void G1ConcurrentMark::scan_root_region(const MemRegion* region, uint worker_id) class G1CMRootRegionScanTask : public WorkerTask { G1ConcurrentMark* _cm; + bool _should_yield; + public: - G1CMRootRegionScanTask(G1ConcurrentMark* cm) : - WorkerTask("G1 Root Region Scan"), _cm(cm) { } + G1CMRootRegionScanTask(G1ConcurrentMark* cm, bool should_yield) : + WorkerTask("G1 Root Region Scan"), _cm(cm), _should_yield(should_yield) { } void work(uint worker_id) { - G1CMRootMemRegions* root_regions = _cm->root_regions(); - const MemRegion* region = root_regions->claim_next(); - while (region != nullptr) { + SuspendibleThreadSetJoiner sts_join(_should_yield); + + while (true) { + if (_cm->has_root_region_scan_aborted()) { + return; + } + G1CMRootMemRegions* root_regions = _cm->root_regions(); + const MemRegion* region = root_regions->claim_next(); + if (region == nullptr) { + return; + } _cm->scan_root_region(region, worker_id); - region = root_regions->claim_next(); + if (_should_yield) { + SuspendibleThreadSet::yield(); + // If we yielded, a GC may have processed all root regions, + // so this loop will naturally exit on the next claim_next() call. + // Same if a Full GC signalled abort of the concurrent mark. + } } } }; -void G1ConcurrentMark::scan_root_regions() { - // scan_in_progress() will have been set to true only if there was - // at least one root region to scan. So, if it's false, we - // should not attempt to do any further work. - if (root_regions()->scan_in_progress()) { - assert(!has_aborted(), "Aborting before root region scanning is finished not supported."); - +bool G1ConcurrentMark::scan_root_regions(WorkerThreads* workers, bool concurrent) { + // We first check whether there is any work to do as we might have already aborted + // the concurrent cycle, or ran into a GC that did the actual work when we reach here. + // We want to avoid spinning up the worker threads if that happened. + // (Note that due to races reading the abort-flag, we might spin up the threads anyway). + // + // Abort happens if a Full GC occurs right after starting the concurrent cycle or + // a young gc doing the work. + // + // Concurrent gc threads enter an STS when starting the task, so they stop, then + // continue after that safepoint. + // + // Must not use G1CMRootMemRegions::work_completed() here because we need to get a + // consistent view of the value containing the number of remaining regions across the + // usages below. The safepoint/gc may already be running and modifying it + // while this code is still executing. + uint num_remaining = root_regions()->num_remaining_regions(); + bool do_scan = num_remaining > 0 && !has_root_region_scan_aborted(); + if (do_scan) { // Assign one worker to each root-region but subject to the max constraint. - const uint num_workers = MIN2(root_regions()->num_root_regions(), + // The constraint is also important to avoid accesses beyond the allocated per-worker + // marking helper data structures. We might get passed different WorkerThreads with + // different number of threads (potential worker ids) than helper data structures when + // completing this work during GC. + const uint num_workers = MIN2(num_remaining, _max_concurrent_workers); + assert(num_workers > 0, "no more remaining root regions to process"); - G1CMRootRegionScanTask task(this); + G1CMRootRegionScanTask task(this, concurrent); log_debug(gc, ergo)("Running %s using %u workers for %u work units.", - task.name(), num_workers, root_regions()->num_root_regions()); - _concurrent_workers->run_task(&task, num_workers); - - // It's possible that has_aborted() is true here without actually - // aborting the survivor scan earlier. This is OK as it's - // mainly used for sanity checking. - root_regions()->scan_finished(); + task.name(), num_workers, num_remaining); + workers->run_task(&task, num_workers); } + + // At the end of this method, we can re-read num_remaining() in the assert: either + // we got non-zero above and we processed all root regions (and it must be zero + // after the worker task synchronization) or it had already been zero. We also + // can't have started another concurrent cycle that could have set it to something else + // while still in the concurrent cycle (if called concurrently). + assert_root_region_scan_completed_or_aborted(); + + return do_scan; } -bool G1ConcurrentMark::wait_until_root_region_scan_finished() { - return root_regions()->wait_until_scan_finished(); +void G1ConcurrentMark::scan_root_regions_concurrently() { + assert(Thread::current() == cm_thread(), "must be on Concurrent Mark Thread"); + scan_root_regions(_concurrent_workers, true /* concurrent */); +} + +bool G1ConcurrentMark::complete_root_regions_scan_in_safepoint() { + assert_at_safepoint_on_vm_thread(); + return scan_root_regions(_g1h->workers(), false /* concurrent */); } void G1ConcurrentMark::add_root_region(G1HeapRegion* r) { @@ -1169,9 +1182,16 @@ bool G1ConcurrentMark::is_root_region(G1HeapRegion* r) { return root_regions()->contains(MemRegion(top_at_mark_start(r), r->top())); } -void G1ConcurrentMark::root_region_scan_abort_and_wait() { - root_regions()->abort(); - root_regions()->wait_until_scan_finished(); +void G1ConcurrentMark::abort_root_region_scan() { + assert_not_at_safepoint(); + + _root_region_scan_aborted.store_relaxed(true); +} + +void G1ConcurrentMark::abort_root_region_scan_at_safepoint() { + assert_at_safepoint_on_vm_thread(); + + _root_region_scan_aborted.store_relaxed(true); } void G1ConcurrentMark::concurrent_cycle_start() { @@ -1948,12 +1968,15 @@ void G1ConcurrentMark::print_stats() { } bool G1ConcurrentMark::concurrent_cycle_abort() { + assert_at_safepoint_on_vm_thread(); + assert(_g1h->collector_state()->is_in_full_gc(), "must be"); + // If we start the compaction before the CM threads finish // scanning the root regions we might trip them over as we'll - // be moving objects / updating references. So let's wait until - // they are done. By telling them to abort, they should complete - // early. - root_region_scan_abort_and_wait(); + // be moving objects / updating references. Since the root region + // scan synchronized with the safepoint, just tell it to abort. + // It will notice when the threads start up again later. + abort_root_region_scan_at_safepoint(); // We haven't started a concurrent cycle no need to do anything; we might have // aborted the marking because of shutting down though. In this case the marking @@ -1983,7 +2006,7 @@ bool G1ConcurrentMark::concurrent_cycle_abort() { } void G1ConcurrentMark::abort_marking_threads() { - assert(!_root_regions.scan_in_progress(), "still doing root region scan"); + assert_root_region_scan_completed_or_aborted(); _has_aborted.store_relaxed(true); _first_overflow_barrier_sync.abort(); _second_overflow_barrier_sync.abort(); @@ -2150,8 +2173,7 @@ void G1CMTask::reset_for_restart() { void G1CMTask::register_partial_array_splitter() { ::new (&_partial_array_splitter) PartialArraySplitter(_cm->partial_array_state_manager(), - _cm->max_num_tasks(), - ObjArrayMarkingStride); + _cm->max_num_tasks()); } void G1CMTask::unregister_partial_array_splitter() { @@ -2329,20 +2351,16 @@ void G1CMTask::drain_local_queue(bool partially) { } } -size_t G1CMTask::start_partial_array_processing(oop obj) { - assert(should_be_sliced(obj), "Must be an array object %d and large %zu", obj->is_objArray(), obj->size()); - - objArrayOop obj_array = objArrayOop(obj); - size_t array_length = obj_array->length(); - - size_t initial_chunk_size = _partial_array_splitter.start(_task_queue, obj_array, nullptr, array_length); +size_t G1CMTask::start_partial_array_processing(objArrayOop obj) { + assert(obj->length() >= (int)ObjArrayMarkingStride, "Must be a large array object %d", obj->length()); // Mark objArray klass metadata - if (_cm_oop_closure->do_metadata()) { - _cm_oop_closure->do_klass(obj_array->klass()); - } + process_klass(obj->klass()); - process_array_chunk(obj_array, 0, initial_chunk_size); + size_t array_length = obj->length(); + size_t initial_chunk_size = _partial_array_splitter.start(_task_queue, obj, nullptr, array_length, ObjArrayMarkingStride); + + process_array_chunk(obj, 0, initial_chunk_size); // Include object header size return objArrayOopDesc::object_size(checked_cast(initial_chunk_size)); @@ -2898,7 +2916,7 @@ G1CMTask::G1CMTask(uint worker_id, _cm(cm), _mark_bitmap(nullptr), _task_queue(task_queue), - _partial_array_splitter(_cm->partial_array_state_manager(), _cm->max_num_tasks(), ObjArrayMarkingStride), + _partial_array_splitter(_cm->partial_array_state_manager(), _cm->max_num_tasks()), _mark_stats_cache(mark_stats, G1RegionMarkStatsCache::RegionMarkStatsCacheSize), _calls(0), _time_target_ms(0.0), diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp index de97179d210..f9287f673d2 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp @@ -288,57 +288,36 @@ private: class G1CMRootMemRegions { // The set of root MemRegions. MemRegion* _root_regions; - size_t const _max_regions; + uint const _max_regions; - Atomic _num_root_regions; // Actual number of root regions. + Atomic _num_regions; // Actual number of root regions. + Atomic _num_claimed_regions; // Number of root regions currently claimed. - Atomic _claimed_root_regions; // Number of root regions currently claimed. - - Atomic _scan_in_progress; - Atomic _should_abort; - - void notify_scan_done(); + uint num_regions() const { return _num_regions.load_relaxed(); } + uint num_claimed_regions() const { return _num_claimed_regions.load_relaxed(); } public: G1CMRootMemRegions(uint const max_regions); ~G1CMRootMemRegions(); - // Reset the data structure to allow addition of new root regions. - void reset(); - void add(HeapWord* start, HeapWord* end); - // Reset the claiming / scanning of the root regions. - void prepare_for_scan(); - - // Forces get_next() to return null so that the iteration aborts early. - void abort() { _should_abort.store_relaxed(true); } - - // Return true if the CM thread are actively scanning root regions, - // false otherwise. - bool scan_in_progress() { return _scan_in_progress.load_relaxed(); } + // Reset data structure to initial state. + void reset(); // Claim the next root MemRegion to scan atomically, or return null if // all have been claimed. const MemRegion* claim_next(); - // The number of root regions to scan. - uint num_root_regions() const; + // Number of root regions to still process. + uint num_remaining_regions() const; + + // Returns whether all root regions have been processed or the processing been aborted. + bool work_completed() const; // Is the given memregion contained in the root regions; the MemRegion must // match exactly. bool contains(const MemRegion mr) const; - - void cancel_scan(); - - // Flag that we're done with root region scanning and notify anyone - // who's waiting on it. If aborted is false, assume that all regions - // have been claimed. - void scan_finished(); - - // If CM threads are still scanning root regions, wait until they - // are done. Return true if we had to wait, false otherwise. - bool wait_until_scan_finished(); }; // This class manages data structures and methods for doing liveness analysis in @@ -367,6 +346,7 @@ class G1ConcurrentMark : public CHeapObj { // Root region tracking and claiming G1CMRootMemRegions _root_regions; + Atomic _root_region_scan_aborted; // For grey objects G1CMMarkStack _global_mark_stack; // Grey objects behind global finger @@ -600,7 +580,7 @@ public: // Notifies marking threads to abort. This is a best-effort notification. Does not // guarantee or update any state after the call. Root region scan must not be - // running. + // running or being aborted. void abort_marking_threads(); // Total cpu time spent in mark worker threads in seconds. @@ -651,17 +631,30 @@ public: // Stop active components/the concurrent mark thread. void stop(); - // Scan all the root regions and mark everything reachable from - // them. - void scan_root_regions(); - bool wait_until_root_region_scan_finished(); void add_root_region(G1HeapRegion* r); bool is_root_region(G1HeapRegion* r); - void root_region_scan_abort_and_wait(); + + // Scan all the root regions concurrently and mark everything reachable from + // them. + void scan_root_regions_concurrently(); + // Complete root region scan work in the safepoint, return if we did some work. + bool complete_root_regions_scan_in_safepoint(); + + // Abort an active concurrent root region scan outside safepoint. + void abort_root_region_scan(); + + bool has_root_region_scan_aborted() const; private: + // Abort an active concurrent root region scan during safepoint. + void abort_root_region_scan_at_safepoint(); + + void assert_root_region_scan_completed_or_aborted() PRODUCT_RETURN; G1CMRootMemRegions* root_regions() { return &_root_regions; } + // Perform root region scan until all root regions have been processed, or + // the process has been aborted. Returns true if we did some work. + bool scan_root_regions(WorkerThreads* workers, bool concurrent); // Scan a single root MemRegion to mark everything reachable from it. void scan_root_region(const MemRegion* region, uint worker_id); @@ -851,12 +844,10 @@ private: // mark bitmap scan, and so needs to be pushed onto the mark stack. bool is_below_finger(oop obj, HeapWord* global_finger) const; - template void process_grey_task_entry(G1TaskQueueEntry task_entry, bool stolen); - static bool should_be_sliced(oop obj); // Start processing the given objArrayOop by first pushing its continuations and // then scanning the first chunk including the header. - size_t start_partial_array_processing(oop obj); + size_t start_partial_array_processing(objArrayOop obj); // Process the given continuation. Returns the number of words scanned. size_t process_partial_array(const G1TaskQueueEntry& task, bool stolen); // Apply the closure to the given range of elements in the objArray. @@ -925,6 +916,9 @@ public: template inline bool deal_with_reference(T* p); + // Scan the klass and visit its children. + inline void process_klass(Klass* klass); + // Scans an object and visits its children. inline void process_entry(G1TaskQueueEntry task_entry, bool stolen); diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.inline.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.inline.hpp index 21167d5cae9..094f4dca994 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.inline.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.inline.hpp @@ -106,9 +106,27 @@ inline void G1CMMarkStack::iterate(Fn fn) const { } #endif +inline void G1CMTask::process_klass(Klass* klass) { + _cm_oop_closure->do_klass(klass); +} + // It scans an object and visits its children. inline void G1CMTask::process_entry(G1TaskQueueEntry task_entry, bool stolen) { - process_grey_task_entry(task_entry, stolen); + assert(task_entry.is_partial_array_state() || _mark_bitmap->is_marked(cast_from_oop(task_entry.to_oop())), + "Any stolen object should be a slice or marked"); + + if (task_entry.is_partial_array_state()) { + _words_scanned += process_partial_array(task_entry, stolen); + } else { + oop obj = task_entry.to_oop(); + if (should_be_sliced(obj)) { + _words_scanned += start_partial_array_processing(objArrayOop(obj)); + } else { + _words_scanned += obj->oop_iterate_size(_cm_oop_closure); + } + } + + check_limits(); } inline void G1CMTask::push(G1TaskQueueEntry task_entry) { @@ -160,27 +178,6 @@ inline bool G1CMTask::is_below_finger(oop obj, HeapWord* global_finger) const { return objAddr < global_finger; } -template -inline void G1CMTask::process_grey_task_entry(G1TaskQueueEntry task_entry, bool stolen) { - assert(scan || (!task_entry.is_partial_array_state() && task_entry.to_oop()->is_typeArray()), "Skipping scan of grey non-typeArray"); - assert(task_entry.is_partial_array_state() || _mark_bitmap->is_marked(cast_from_oop(task_entry.to_oop())), - "Any stolen object should be a slice or marked"); - - if (scan) { - if (task_entry.is_partial_array_state()) { - _words_scanned += process_partial_array(task_entry, stolen); - } else { - oop obj = task_entry.to_oop(); - if (should_be_sliced(obj)) { - _words_scanned += start_partial_array_processing(obj); - } else { - _words_scanned += obj->oop_iterate_size(_cm_oop_closure); - } - } - } - check_limits(); -} - inline bool G1CMTask::should_be_sliced(oop obj) { return obj->is_objArray() && ((objArrayOop)obj)->length() >= (int)ObjArrayMarkingStride; } @@ -272,7 +269,6 @@ inline bool G1CMTask::make_reference_grey(oop obj) { // be pushed on the stack. So, some duplicate work, but no // correctness problems. if (is_below_finger(obj, global_finger)) { - G1TaskQueueEntry entry(obj); if (obj->is_typeArray()) { // Immediately process arrays of primitive types, rather // than pushing on the mark stack. This keeps us from @@ -284,8 +280,8 @@ inline bool G1CMTask::make_reference_grey(oop obj) { // by only doing a bookkeeping update and avoiding the // actual scan of the object - a typeArray contains no // references, and the metadata is built-in. - process_grey_task_entry(entry, false /* stolen */); } else { + G1TaskQueueEntry entry(obj); push(entry); } } diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.cpp index 31d61b8b388..b8c97acd1b0 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.cpp @@ -131,14 +131,11 @@ void G1ConcurrentMarkThread::run_service() { update_perf_counter_cpu_time(); } - _cm->root_regions()->cancel_scan(); } void G1ConcurrentMarkThread::stop_service() { if (is_in_progress()) { - // We are not allowed to abort the marking threads during root region scan. - // Needs to be done separately. - _cm->root_region_scan_abort_and_wait(); + _cm->abort_root_region_scan(); _cm->abort_marking_threads(); } @@ -164,7 +161,7 @@ bool G1ConcurrentMarkThread::phase_clear_cld_claimed_marks() { bool G1ConcurrentMarkThread::phase_scan_root_regions() { G1ConcPhaseTimer p(_cm, "Concurrent Scan Root Regions"); - _cm->scan_root_regions(); + _cm->scan_root_regions_concurrently(); update_perf_counter_cpu_time(); return _cm->has_aborted(); } diff --git a/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp b/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp index 8546e6e2d64..e12a8c284de 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, 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 @@ -326,11 +326,14 @@ bool G1ConcurrentRefineSweepState::complete_work(bool concurrent, bool print_log if (print_log) { G1ConcurrentRefineStats* s = &_stats; - log_debug(gc, refine)("Refinement took %.2fms (pre-sweep %.2fms card refine %.2f) " + State state_bounded_by_sweeprt = (_state == State::SweepRT || _state == State::CompleteRefineWork) + ? State::SweepRT : _state; + + log_debug(gc, refine)("Refinement took %.2fms (pre-sweep %.2fms card refine %.2fms) " "(scanned %zu clean %zu (%.2f%%) not_clean %zu (%.2f%%) not_parsable %zu " "refers_to_cset %zu (%.2f%%) still_refers_to_cset %zu (%.2f%%) no_cross_region %zu pending %zu)", get_duration(State::Idle, _state).seconds() * 1000.0, - get_duration(State::Idle, State::SweepRT).seconds() * 1000.0, + get_duration(State::Idle, state_bounded_by_sweeprt).seconds() * 1000.0, TimeHelper::counter_to_millis(s->refine_duration()), s->cards_scanned(), s->cards_clean(), diff --git a/src/hotspot/share/gc/g1/g1ConcurrentStartToMixedTimeTracker.hpp b/src/hotspot/share/gc/g1/g1ConcurrentStartToMixedTimeTracker.hpp index 57372e695c8..f8bad4bdcd7 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentStartToMixedTimeTracker.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentStartToMixedTimeTracker.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, 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 @@ -30,7 +30,7 @@ // Used to track time from the end of concurrent start to the first mixed GC. // After calling the concurrent start/mixed gc notifications, the result can be -// obtained in last_marking_time() once, after which the tracking resets. +// obtained in get_and_reset_last_marking_time() once, after which the tracking resets. // Any pauses recorded by add_pause() will be subtracted from that results. class G1ConcurrentStartToMixedTimeTracker { private: @@ -60,7 +60,7 @@ public: } } - double last_marking_time() { + double get_and_reset_last_marking_time() { assert(has_result(), "Do not have all measurements yet."); double result = (_mixed_start_time - _concurrent_start_end_time) - _total_pause_time; reset(); @@ -80,6 +80,8 @@ public: } } + bool is_active() const { return _active; } + // Returns whether we have a result that can be retrieved. bool has_result() const { return _mixed_start_time > 0.0 && _concurrent_start_end_time > 0.0; } }; diff --git a/src/hotspot/share/gc/g1/g1EvacFailureRegions.cpp b/src/hotspot/share/gc/g1/g1EvacFailureRegions.cpp index 37553e2aa56..03c20346eb3 100644 --- a/src/hotspot/share/gc/g1/g1EvacFailureRegions.cpp +++ b/src/hotspot/share/gc/g1/g1EvacFailureRegions.cpp @@ -55,7 +55,7 @@ void G1EvacFailureRegions::post_collection() { _regions_pinned.resize(0); _regions_alloc_failed.resize(0); - FREE_C_HEAP_ARRAY(uint, _evac_failed_regions); + FREE_C_HEAP_ARRAY(_evac_failed_regions); _evac_failed_regions = nullptr; } diff --git a/src/hotspot/share/gc/g1/g1FullCollector.cpp b/src/hotspot/share/gc/g1/g1FullCollector.cpp index c835dd159a6..8b38509d1d8 100644 --- a/src/hotspot/share/gc/g1/g1FullCollector.cpp +++ b/src/hotspot/share/gc/g1/g1FullCollector.cpp @@ -167,10 +167,10 @@ G1FullCollector::~G1FullCollector() { delete _partial_array_state_manager; - FREE_C_HEAP_ARRAY(G1FullGCMarker*, _markers); - FREE_C_HEAP_ARRAY(G1FullGCCompactionPoint*, _compaction_points); - FREE_C_HEAP_ARRAY(Atomic, _compaction_tops); - FREE_C_HEAP_ARRAY(G1RegionMarkStats, _live_stats); + FREE_C_HEAP_ARRAY(_markers); + FREE_C_HEAP_ARRAY(_compaction_points); + FREE_C_HEAP_ARRAY(_compaction_tops); + FREE_C_HEAP_ARRAY(_live_stats); } class PrepareRegionsClosure : public G1HeapRegionClosure { diff --git a/src/hotspot/share/gc/g1/g1FullGCCompactTask.cpp b/src/hotspot/share/gc/g1/g1FullGCCompactTask.cpp index 5dbf70f36b3..93d8da0d842 100644 --- a/src/hotspot/share/gc/g1/g1FullGCCompactTask.cpp +++ b/src/hotspot/share/gc/g1/g1FullGCCompactTask.cpp @@ -22,7 +22,7 @@ * */ -#include "gc/g1/g1CollectedHeap.hpp" +#include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1ConcurrentMarkBitMap.inline.hpp" #include "gc/g1/g1FullCollector.inline.hpp" #include "gc/g1/g1FullGCCompactionPoint.hpp" diff --git a/src/hotspot/share/gc/g1/g1FullGCMarker.cpp b/src/hotspot/share/gc/g1/g1FullGCMarker.cpp index 2b0b78ac1ce..3be4ab8d839 100644 --- a/src/hotspot/share/gc/g1/g1FullGCMarker.cpp +++ b/src/hotspot/share/gc/g1/g1FullGCMarker.cpp @@ -39,7 +39,7 @@ G1FullGCMarker::G1FullGCMarker(G1FullCollector* collector, _worker_id(worker_id), _bitmap(collector->mark_bitmap()), _task_queue(), - _partial_array_splitter(collector->partial_array_state_manager(), collector->workers(), ObjArrayMarkingStride), + _partial_array_splitter(collector->partial_array_state_manager(), collector->workers()), _mark_closure(worker_id, this, ClassLoaderData::_claim_stw_fullgc_mark, G1CollectedHeap::heap()->ref_processor_stw()), _stack_closure(this), _cld_closure(mark_closure(), ClassLoaderData::_claim_stw_fullgc_mark), @@ -60,14 +60,26 @@ void G1FullGCMarker::process_partial_array(PartialArrayState* state, bool stolen process_array_chunk(obj_array, claim._start, claim._end); } +static uintx calc_array_stride(uint array_len, uint num_threads) { + precond(num_threads > 0); + + const size_t stride = (array_len + num_threads - 1) / num_threads; + return clamp(stride, ArrayMarkingMinStride, ObjArrayMarkingStride); +} + void G1FullGCMarker::start_partial_array_processing(objArrayOop obj) { mark_closure()->do_klass(obj->klass()); // Don't push empty arrays to avoid unnecessary work. - size_t array_length = obj->length(); - if (array_length > 0) { - size_t initial_chunk_size = _partial_array_splitter.start(task_queue(), obj, nullptr, array_length); - process_array_chunk(obj, 0, initial_chunk_size); + const int array_length = obj->length(); + + if (array_length == 0) { + return; } + + const uintx stride = calc_array_stride(array_length, _collector->workers()); + const size_t initial_chunk_size = _partial_array_splitter.start(task_queue(), obj, nullptr, array_length, stride); + + process_array_chunk(obj, 0, initial_chunk_size); } void G1FullGCMarker::complete_marking(G1ScannerTasksQueueSet* task_queues, diff --git a/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp b/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp index a5013ddbb40..e13b9d91bc5 100644 --- a/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp +++ b/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp @@ -180,8 +180,7 @@ void G1GCPhaseTimes::reset() { _cur_post_evacuate_cleanup_2_time_ms = 0.0; _cur_resize_heap_time_ms = 0.0; _cur_ref_proc_time_ms = 0.0; - _root_region_scan_wait_time_ms = 0.0; - _external_accounted_time_ms = 0.0; + _root_region_scan_time_ms = 0.0; _recorded_prepare_heap_roots_time_ms = 0.0; _recorded_young_cset_choice_time_ms = 0.0; _recorded_non_young_cset_choice_time_ms = 0.0; @@ -416,8 +415,6 @@ double G1GCPhaseTimes::print_pre_evacuate_collection_set() const { info_time("Pre Evacuate Collection Set", sum_ms); - // Concurrent tasks of ResetMarkingState and NoteStartOfMark are triggered during - // young collection. However, their execution time are not included in _gc_pause_time_ms. if (_cur_prepare_concurrent_task_time_ms > 0.0) { debug_time("Prepare Concurrent Start", _cur_prepare_concurrent_task_time_ms); debug_phase(_gc_par_phases[ResetMarkingState], 1); @@ -543,14 +540,13 @@ void G1GCPhaseTimes::print_other(double accounted_ms) const { info_time("Other", _gc_pause_time_ms - accounted_ms); } -// Root-region-scan-wait, verify-before and verify-after are part of young GC, +// Root region scan, verify before and verify after are part of young GC, // but these are not measured by G1Policy. i.e. these are not included in // G1Policy::record_young_collection_start() and record_young_collection_end(). -// In addition, these are not included in G1GCPhaseTimes::_gc_pause_time_ms. // See G1YoungCollector::collect(). void G1GCPhaseTimes::print(bool evacuation_failed) { - if (_root_region_scan_wait_time_ms > 0.0) { - debug_time("Root Region Scan Waiting", _root_region_scan_wait_time_ms); + if (_root_region_scan_time_ms > 0.0) { + debug_time("Root Region Scan", _root_region_scan_time_ms); } // Check if some time has been recorded for verification and only then print diff --git a/src/hotspot/share/gc/g1/g1GCPhaseTimes.hpp b/src/hotspot/share/gc/g1/g1GCPhaseTimes.hpp index 8223148b791..eb51b340da3 100644 --- a/src/hotspot/share/gc/g1/g1GCPhaseTimes.hpp +++ b/src/hotspot/share/gc/g1/g1GCPhaseTimes.hpp @@ -175,7 +175,6 @@ class G1GCPhaseTimes : public CHeapObj { double _cur_collection_nmethod_list_cleanup_time_ms; double _cur_merge_heap_roots_time_ms; - // Merge refinement table time. Note that this time is included in _cur_merge_heap_roots_time_ms. double _cur_merge_refinement_table_time_ms; double _cur_optional_merge_heap_roots_time_ms; @@ -190,10 +189,7 @@ class G1GCPhaseTimes : public CHeapObj { double _cur_resize_heap_time_ms; double _cur_ref_proc_time_ms; - // Not included in _gc_pause_time_ms - double _root_region_scan_wait_time_ms; - - double _external_accounted_time_ms; + double _root_region_scan_time_ms; double _recorded_prepare_heap_roots_time_ms; @@ -210,7 +206,6 @@ class G1GCPhaseTimes : public CHeapObj { double _cur_region_register_time; - // Not included in _gc_pause_time_ms double _cur_verify_before_time_ms; double _cur_verify_after_time_ms; @@ -298,7 +293,7 @@ class G1GCPhaseTimes : public CHeapObj { } void record_merge_heap_roots_time(double ms) { - _cur_merge_heap_roots_time_ms += ms; + _cur_merge_heap_roots_time_ms = ms; } void record_merge_refinement_table_time(double ms) { @@ -325,8 +320,8 @@ class G1GCPhaseTimes : public CHeapObj { _cur_prepare_concurrent_task_time_ms = ms; } - void record_root_region_scan_wait_time(double time_ms) { - _root_region_scan_wait_time_ms = time_ms; + void record_root_region_scan_time(double time_ms) { + _root_region_scan_time_ms = time_ms; } void record_serial_free_cset_time_ms(double time_ms) { @@ -373,10 +368,6 @@ class G1GCPhaseTimes : public CHeapObj { _cur_verify_after_time_ms = time_ms; } - void inc_external_accounted_time_ms(double time_ms) { - _external_accounted_time_ms += time_ms; - } - void record_prepare_heap_roots_time_ms(double recorded_prepare_heap_roots_time_ms) { _recorded_prepare_heap_roots_time_ms = recorded_prepare_heap_roots_time_ms; } @@ -399,8 +390,8 @@ class G1GCPhaseTimes : public CHeapObj { return _cur_resize_heap_time_ms; } - double root_region_scan_wait_time_ms() { - return _root_region_scan_wait_time_ms; + double root_region_scan_time_ms() { + return _root_region_scan_time_ms; } double young_cset_choice_time_ms() { diff --git a/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp b/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp index 3c0318827ef..214f1a2d2b6 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp @@ -718,7 +718,7 @@ G1HeapRegionClaimer::G1HeapRegionClaimer(uint n_workers) : } G1HeapRegionClaimer::~G1HeapRegionClaimer() { - FREE_C_HEAP_ARRAY(uint, _claims); + FREE_C_HEAP_ARRAY(_claims); } uint G1HeapRegionClaimer::offset_for_worker(uint worker_id) const { @@ -759,7 +759,7 @@ public: for (uint worker = 0; worker < _num_workers; worker++) { _worker_freelists[worker].~G1FreeRegionList(); } - FREE_C_HEAP_ARRAY(G1FreeRegionList, _worker_freelists); + FREE_C_HEAP_ARRAY(_worker_freelists); } G1FreeRegionList* worker_freelist(uint worker) { diff --git a/src/hotspot/share/gc/g1/g1HeapRegionSet.cpp b/src/hotspot/share/gc/g1/g1HeapRegionSet.cpp index 70186adcdfc..930a4bd953f 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionSet.cpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionSet.cpp @@ -384,7 +384,7 @@ G1FreeRegionList::NodeInfo::NodeInfo() : _numa(G1NUMA::numa()), _length_of_node( } G1FreeRegionList::NodeInfo::~NodeInfo() { - FREE_C_HEAP_ARRAY(uint, _length_of_node); + FREE_C_HEAP_ARRAY(_length_of_node); } void G1FreeRegionList::NodeInfo::clear() { diff --git a/src/hotspot/share/gc/g1/g1HeapTransition.cpp b/src/hotspot/share/gc/g1/g1HeapTransition.cpp index 30ad4c72bf6..690bda4e7e6 100644 --- a/src/hotspot/share/gc/g1/g1HeapTransition.cpp +++ b/src/hotspot/share/gc/g1/g1HeapTransition.cpp @@ -55,8 +55,8 @@ G1HeapTransition::Data::Data(G1CollectedHeap* g1_heap) : } G1HeapTransition::Data::~Data() { - FREE_C_HEAP_ARRAY(uint, _eden_length_per_node); - FREE_C_HEAP_ARRAY(uint, _survivor_length_per_node); + FREE_C_HEAP_ARRAY(_eden_length_per_node); + FREE_C_HEAP_ARRAY(_survivor_length_per_node); } G1HeapTransition::G1HeapTransition(G1CollectedHeap* g1_heap) : _g1_heap(g1_heap), _before(g1_heap) { } diff --git a/src/hotspot/share/gc/g1/g1HeapVerifier.cpp b/src/hotspot/share/gc/g1/g1HeapVerifier.cpp index dd7a8aa117d..714a2473a08 100644 --- a/src/hotspot/share/gc/g1/g1HeapVerifier.cpp +++ b/src/hotspot/share/gc/g1/g1HeapVerifier.cpp @@ -25,6 +25,7 @@ #include "code/nmethod.hpp" #include "gc/g1/g1Allocator.inline.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" +#include "gc/g1/g1CollectorState.inline.hpp" #include "gc/g1/g1ConcurrentMarkThread.hpp" #include "gc/g1/g1HeapRegion.inline.hpp" #include "gc/g1/g1HeapRegionRemSet.hpp" diff --git a/src/hotspot/share/gc/g1/g1IHOPControl.cpp b/src/hotspot/share/gc/g1/g1IHOPControl.cpp index 43698e9f12b..1e1c52477f9 100644 --- a/src/hotspot/share/gc/g1/g1IHOPControl.cpp +++ b/src/hotspot/share/gc/g1/g1IHOPControl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -38,18 +38,18 @@ double G1IHOPControl::predict(const TruncatedSeq* seq) const { bool G1IHOPControl::have_enough_data_for_prediction() const { assert(_is_adaptive, "precondition"); - return ((size_t)_marking_times_s.num() >= G1AdaptiveIHOPNumInitialSamples) && - ((size_t)_allocation_rate_s.num() >= G1AdaptiveIHOPNumInitialSamples); + return ((size_t)_marking_start_to_mixed_time_s.num() >= G1AdaptiveIHOPNumInitialSamples) && + ((size_t)_old_gen_alloc_rate.num() >= G1AdaptiveIHOPNumInitialSamples); } -double G1IHOPControl::last_marking_length_s() const { - return _marking_times_s.last(); +double G1IHOPControl::last_marking_start_to_mixed_time_s() const { + return _marking_start_to_mixed_time_s.last(); } -size_t G1IHOPControl::actual_target_threshold() const { +size_t G1IHOPControl::effective_target_occupancy() const { assert(_is_adaptive, "precondition"); - // The actual target threshold takes the heap reserve and the expected waste in + // The effective target occupancy takes the heap reserve and the expected waste in // free space into account. // _heap_reserve is that part of the total heap capacity that is reserved for // eventual promotion failure. @@ -79,9 +79,9 @@ G1IHOPControl::G1IHOPControl(double ihop_percent, _last_allocation_time_s(0.0), _old_gen_alloc_tracker(old_gen_alloc_tracker), _predictor(predictor), - _marking_times_s(10, 0.05), - _allocation_rate_s(10, 0.05), - _last_unrestrained_young_size(0) { + _marking_start_to_mixed_time_s(10, 0.05), + _old_gen_alloc_rate(10, 0.05), + _expected_young_gen_at_first_mixed_gc(0) { assert(_initial_ihop_percent >= 0.0 && _initial_ihop_percent <= 100.0, "IHOP percent out of range: %.3f", ihop_percent); assert(!_is_adaptive || _predictor != nullptr, "precondition"); @@ -98,85 +98,104 @@ void G1IHOPControl::report_statistics(G1NewTracer* new_tracer, size_t non_young_ send_trace_event(new_tracer, non_young_occupancy); } -void G1IHOPControl::update_allocation_info(double allocation_time_s, size_t additional_buffer_size) { +void G1IHOPControl::update_allocation_info(double allocation_time_s, size_t expected_young_gen_size) { assert(allocation_time_s > 0, "Invalid allocation time: %.3f", allocation_time_s); _last_allocation_time_s = allocation_time_s; double alloc_rate = _old_gen_alloc_tracker->last_period_old_gen_growth() / allocation_time_s; - _allocation_rate_s.add(alloc_rate); - _last_unrestrained_young_size = additional_buffer_size; + _old_gen_alloc_rate.add(alloc_rate); + _expected_young_gen_at_first_mixed_gc = expected_young_gen_size; } -void G1IHOPControl::update_marking_length(double marking_length_s) { - assert(marking_length_s >= 0.0, "Invalid marking length: %.3f", marking_length_s); - _marking_times_s.add(marking_length_s); +void G1IHOPControl::add_marking_start_to_mixed_length(double length_s) { + assert(length_s >= 0.0, "Invalid marking length: %.3f", length_s); + _marking_start_to_mixed_time_s.add(length_s); } -size_t G1IHOPControl::get_conc_mark_start_threshold() { +// Determine the old generation occupancy threshold at which to start +// concurrent marking such that reclamation (first Mixed GC) begins +// before the heap reaches a critical occupancy level. +size_t G1IHOPControl::old_gen_threshold_for_conc_mark_start() { guarantee(_target_occupancy > 0, "Target occupancy must be initialized"); if (!_is_adaptive || !have_enough_data_for_prediction()) { return (size_t)(_initial_ihop_percent * _target_occupancy / 100.0); } - double pred_marking_time = predict(&_marking_times_s); - double pred_rate = predict(&_allocation_rate_s); - size_t pred_bytes = (size_t)(pred_marking_time * pred_rate); - size_t predicted_needed = pred_bytes + _last_unrestrained_young_size; - size_t internal_threshold = actual_target_threshold(); + // During the time between marking start and the first Mixed GC, + // additional memory will be consumed: + // - Old gen grows due to allocations: + // old_gen_alloc_bytes = old_gen_alloc_rate * marking_start_to_mixed_time + // - Young gen will occupy a certain size at the first Mixed GC: + // expected_young_gen_at_first_mixed_gc + double marking_start_to_mixed_time = predict(&_marking_start_to_mixed_time_s); + double old_gen_alloc_rate = predict(&_old_gen_alloc_rate); + size_t old_gen_alloc_bytes = (size_t)(marking_start_to_mixed_time * old_gen_alloc_rate); - return predicted_needed < internal_threshold - ? internal_threshold - predicted_needed + // Therefore, the total heap occupancy at the first Mixed GC is: + // current_old_gen + old_gen_growth + expected_young_gen_at_first_mixed_gc + // + // To ensure this does not exceed the target_heap_occupancy, we work + // backwards to compute the old gen occupancy at which marking must start: + // mark_start_threshold = target_heap_occupancy - + // (old_gen_growth + expected_young_gen_at_first_mixed_gc) + + size_t predicted_needed = old_gen_alloc_bytes + _expected_young_gen_at_first_mixed_gc; + size_t target_heap_occupancy = effective_target_occupancy(); + + return predicted_needed < target_heap_occupancy + ? target_heap_occupancy - predicted_needed : 0; } void G1IHOPControl::print_log(size_t non_young_occupancy) { assert(_target_occupancy > 0, "Target occupancy still not updated yet."); - size_t cur_conc_mark_start_threshold = get_conc_mark_start_threshold(); - log_debug(gc, ihop)("Basic information (value update), threshold: %zuB (%1.2f), target occupancy: %zuB, non-young occupancy: %zuB, " - "recent allocation size: %zuB, recent allocation duration: %1.2fms, recent old gen allocation rate: %1.2fB/s, recent marking phase length: %1.2fms", - cur_conc_mark_start_threshold, - percent_of(cur_conc_mark_start_threshold, _target_occupancy), + size_t old_gen_mark_start_threshold = old_gen_threshold_for_conc_mark_start(); + log_debug(gc, ihop)("Basic information (value update), old-gen threshold: %zuB (%1.2f%%), target occupancy: %zuB, old-gen occupancy: %zuB (%1.2f%%), " + "recent old-gen allocation size: %zuB, recent allocation duration: %1.2fms, recent old-gen allocation rate: %1.2fB/s, recent marking phase length: %1.2fms", + old_gen_mark_start_threshold, + percent_of(old_gen_mark_start_threshold, _target_occupancy), _target_occupancy, non_young_occupancy, + percent_of(non_young_occupancy, _target_occupancy), _old_gen_alloc_tracker->last_period_old_gen_bytes(), _last_allocation_time_s * 1000.0, _last_allocation_time_s > 0.0 ? _old_gen_alloc_tracker->last_period_old_gen_bytes() / _last_allocation_time_s : 0.0, - last_marking_length_s() * 1000.0); + last_marking_start_to_mixed_time_s() * 1000.0); if (!_is_adaptive) { return; } - size_t actual_threshold = actual_target_threshold(); - log_debug(gc, ihop)("Adaptive IHOP information (value update), threshold: %zuB (%1.2f), internal target threshold: %zuB, " - "non-young occupancy: %zuB, additional buffer size: %zuB, predicted old gen allocation rate: %1.2fB/s, " - "predicted marking phase length: %1.2fms, prediction active: %s", - cur_conc_mark_start_threshold, - percent_of(cur_conc_mark_start_threshold, actual_threshold), - actual_threshold, + size_t effective_target = effective_target_occupancy(); + log_debug(gc, ihop)("Adaptive IHOP information (value update), prediction active: %s, old-gen threshold: %zuB (%1.2f%%), internal target occupancy: %zuB, " + "old-gen occupancy: %zuB, additional buffer size: %zuB, predicted old-gen allocation rate: %1.2fB/s, " + "predicted marking phase length: %1.2fms", + BOOL_TO_STR(have_enough_data_for_prediction()), + old_gen_mark_start_threshold, + percent_of(old_gen_mark_start_threshold, effective_target), + effective_target, non_young_occupancy, - _last_unrestrained_young_size, - predict(&_allocation_rate_s), - predict(&_marking_times_s) * 1000.0, - have_enough_data_for_prediction() ? "true" : "false"); + _expected_young_gen_at_first_mixed_gc, + predict(&_old_gen_alloc_rate), + predict(&_marking_start_to_mixed_time_s) * 1000.0); } void G1IHOPControl::send_trace_event(G1NewTracer* tracer, size_t non_young_occupancy) { assert(_target_occupancy > 0, "Target occupancy still not updated yet."); - tracer->report_basic_ihop_statistics(get_conc_mark_start_threshold(), + tracer->report_basic_ihop_statistics(old_gen_threshold_for_conc_mark_start(), _target_occupancy, non_young_occupancy, _old_gen_alloc_tracker->last_period_old_gen_bytes(), _last_allocation_time_s, - last_marking_length_s()); + last_marking_start_to_mixed_time_s()); if (_is_adaptive) { - tracer->report_adaptive_ihop_statistics(get_conc_mark_start_threshold(), - actual_target_threshold(), + tracer->report_adaptive_ihop_statistics(old_gen_threshold_for_conc_mark_start(), + effective_target_occupancy(), non_young_occupancy, - _last_unrestrained_young_size, - predict(&_allocation_rate_s), - predict(&_marking_times_s), + _expected_young_gen_at_first_mixed_gc, + predict(&_old_gen_alloc_rate), + predict(&_marking_start_to_mixed_time_s), have_enough_data_for_prediction()); } } diff --git a/src/hotspot/share/gc/g1/g1IHOPControl.hpp b/src/hotspot/share/gc/g1/g1IHOPControl.hpp index 24061c026d1..ff209012f02 100644 --- a/src/hotspot/share/gc/g1/g1IHOPControl.hpp +++ b/src/hotspot/share/gc/g1/g1IHOPControl.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -58,8 +58,11 @@ class G1IHOPControl : public CHeapObj { const G1OldGenAllocationTracker* _old_gen_alloc_tracker; const G1Predictions* _predictor; - TruncatedSeq _marking_times_s; - TruncatedSeq _allocation_rate_s; + // Wall-clock time in seconds from marking start to the first mixed GC, + // excluding GC Pause time. + TruncatedSeq _marking_start_to_mixed_time_s; + // Old generation allocation rate in bytes per second. + TruncatedSeq _old_gen_alloc_rate; // The most recent unrestrained size of the young gen. This is used as an additional // factor in the calculation of the threshold, as the threshold is based on @@ -68,18 +71,18 @@ class G1IHOPControl : public CHeapObj { // Since we cannot know what young gen sizes are used in the future, we will just // use the current one. We expect that this one will be one with a fairly large size, // as there is no marking or mixed gc that could impact its size too much. - size_t _last_unrestrained_young_size; + size_t _expected_young_gen_at_first_mixed_gc; // Get a new prediction bounded below by zero from the given sequence. double predict(const TruncatedSeq* seq) const; bool have_enough_data_for_prediction() const; - double last_marking_length_s() const; + double last_marking_start_to_mixed_time_s() const; - // The "actual" target threshold the algorithm wants to keep during and at the - // end of marking. This is typically lower than the requested threshold, as the + // The "effective" target occupancy the algorithm wants to keep until the start + // of Mixed GCs. This is typically lower than the target occupancy, as the // algorithm needs to consider restrictions by the environment. - size_t actual_target_threshold() const; + size_t effective_target_occupancy() const; void print_log(size_t non_young_occupancy); void send_trace_event(G1NewTracer* tracer, size_t non_young_occupancy); @@ -95,22 +98,24 @@ class G1IHOPControl : public CHeapObj { // Adjust target occupancy. void update_target_occupancy(size_t new_target_occupancy); - // Update information about time during which allocations in the Java heap occurred, - // how large these allocations were in bytes, and an additional buffer. - // The allocations should contain any amount of space made unusable for further - // allocation, e.g. any waste caused by TLAB allocation, space at the end of - // humongous objects that can not be used for allocation, etc. - // Together with the target occupancy, this additional buffer should contain the - // difference between old gen size and total heap size at the start of reclamation, - // and space required for that reclamation. - void update_allocation_info(double allocation_time_s, size_t additional_buffer_size); + void update_target_after_marking_phase(); + + // Update allocation rate information and current expected young gen size for the + // first mixed gc needed for the predictor. Allocation rate is given as the + // separately passed in allocation increment and the time passed (mutator time) + // for the latest allocation increment here. Allocation size is the memory needed + // during the mutator before and the first mixed gc pause itself. + // Contents include young gen at that point, and the memory required for evacuating + // the collection set in that first mixed gc (including waste caused by PLAB + // allocation etc.). + void update_allocation_info(double allocation_time_s, size_t expected_young_gen_size); // Update the time spent in the mutator beginning from the end of concurrent start to // the first mixed gc. - void update_marking_length(double marking_length_s); + void add_marking_start_to_mixed_length(double length_s); // Get the current non-young occupancy at which concurrent marking should start. - size_t get_conc_mark_start_threshold(); + size_t old_gen_threshold_for_conc_mark_start(); void report_statistics(G1NewTracer* tracer, size_t non_young_occupancy); }; diff --git a/src/hotspot/share/gc/g1/g1MonotonicArena.cpp b/src/hotspot/share/gc/g1/g1MonotonicArena.cpp index 3f97870a67f..aea6f4335e8 100644 --- a/src/hotspot/share/gc/g1/g1MonotonicArena.cpp +++ b/src/hotspot/share/gc/g1/g1MonotonicArena.cpp @@ -52,7 +52,7 @@ void G1MonotonicArena::Segment::delete_segment(Segment* segment) { GlobalCounter::write_synchronize(); } segment->~Segment(); - FREE_C_HEAP_ARRAY(_mem_tag, segment); + FREE_C_HEAP_ARRAY(segment); } void G1MonotonicArena::SegmentFreeList::bulk_add(Segment& first, diff --git a/src/hotspot/share/gc/g1/g1MonotonicArenaFreePool.cpp b/src/hotspot/share/gc/g1/g1MonotonicArenaFreePool.cpp index 922c68bfba4..c12321b851a 100644 --- a/src/hotspot/share/gc/g1/g1MonotonicArenaFreePool.cpp +++ b/src/hotspot/share/gc/g1/g1MonotonicArenaFreePool.cpp @@ -159,7 +159,7 @@ G1MonotonicArenaFreePool::~G1MonotonicArenaFreePool() { for (uint i = 0; i < _num_free_lists; i++) { _free_lists[i].~SegmentFreeList(); } - FREE_C_HEAP_ARRAY(mtGC, _free_lists); + FREE_C_HEAP_ARRAY(_free_lists); } G1MonotonicArenaMemoryStats G1MonotonicArenaFreePool::memory_sizes() const { diff --git a/src/hotspot/share/gc/g1/g1NUMA.cpp b/src/hotspot/share/gc/g1/g1NUMA.cpp index 778ed31d7b5..db42b8c10fc 100644 --- a/src/hotspot/share/gc/g1/g1NUMA.cpp +++ b/src/hotspot/share/gc/g1/g1NUMA.cpp @@ -123,8 +123,8 @@ void G1NUMA::initialize(bool use_numa) { G1NUMA::~G1NUMA() { delete _stats; - FREE_C_HEAP_ARRAY(uint, _node_id_to_index_map); - FREE_C_HEAP_ARRAY(uint, _node_ids); + FREE_C_HEAP_ARRAY(_node_id_to_index_map); + FREE_C_HEAP_ARRAY(_node_ids); } void G1NUMA::set_region_info(size_t region_size, size_t page_size) { @@ -280,9 +280,9 @@ G1NodeIndexCheckClosure::~G1NodeIndexCheckClosure() { _ls->print("%u: %u/%u/%u ", numa_ids[i], _matched[i], _mismatched[i], _total[i]); } - FREE_C_HEAP_ARRAY(uint, _matched); - FREE_C_HEAP_ARRAY(uint, _mismatched); - FREE_C_HEAP_ARRAY(uint, _total); + FREE_C_HEAP_ARRAY(_matched); + FREE_C_HEAP_ARRAY(_mismatched); + FREE_C_HEAP_ARRAY(_total); } bool G1NodeIndexCheckClosure::do_heap_region(G1HeapRegion* hr) { diff --git a/src/hotspot/share/gc/g1/g1NUMAStats.cpp b/src/hotspot/share/gc/g1/g1NUMAStats.cpp index aaebfa1be8f..ce62d34f847 100644 --- a/src/hotspot/share/gc/g1/g1NUMAStats.cpp +++ b/src/hotspot/share/gc/g1/g1NUMAStats.cpp @@ -45,9 +45,9 @@ G1NUMAStats::NodeDataArray::NodeDataArray(uint num_nodes) { G1NUMAStats::NodeDataArray::~NodeDataArray() { for (uint row = 0; row < _num_row; row++) { - FREE_C_HEAP_ARRAY(size_t, _data[row]); + FREE_C_HEAP_ARRAY(_data[row]); } - FREE_C_HEAP_ARRAY(size_t*, _data); + FREE_C_HEAP_ARRAY(_data); } void G1NUMAStats::NodeDataArray::create_hit_rate(Stat* result) const { diff --git a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp index cb857dc6eab..50438c641c2 100644 --- a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp +++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp @@ -78,7 +78,7 @@ G1ParScanThreadState::G1ParScanThreadState(G1CollectedHeap* g1h, _surviving_young_words(nullptr), _surviving_words_length(collection_set->young_region_length() + 1), _old_gen_is_full(false), - _partial_array_splitter(g1h->partial_array_state_manager(), num_workers, ParGCArrayScanChunk), + _partial_array_splitter(g1h->partial_array_state_manager(), num_workers), _string_dedup_requests(), _max_num_optional_regions(collection_set->num_optional_regions()), _numa(g1h->numa()), @@ -131,9 +131,9 @@ size_t G1ParScanThreadState::flush_stats(size_t* surviving_young_words, uint num G1ParScanThreadState::~G1ParScanThreadState() { delete _plab_allocator; delete _closures; - FREE_C_HEAP_ARRAY(size_t, _surviving_young_words_base); + FREE_C_HEAP_ARRAY(_surviving_young_words_base); delete[] _oops_into_optional_regions; - FREE_C_HEAP_ARRAY(size_t, _obj_alloc_stat); + FREE_C_HEAP_ARRAY(_obj_alloc_stat); } size_t G1ParScanThreadState::lab_waste_words() const { @@ -253,7 +253,7 @@ void G1ParScanThreadState::start_partial_objarray(oop from_obj, size_t array_length = to_array->length(); size_t initial_chunk_size = // The source array is unused when processing states. - _partial_array_splitter.start(_task_queue, nullptr, to_array, array_length); + _partial_array_splitter.start(_task_queue, nullptr, to_array, array_length, ParGCArrayScanChunk); assert(_scanner.skip_card_mark_set(), "must be"); // Process the initial chunk. No need to process the type in the @@ -650,7 +650,7 @@ oop G1ParScanThreadState::handle_evacuation_failure_par(oop old, markWord m, Kla // Mark the failing object in the marking bitmap and later use the bitmap to handle // evacuation failure recovery. - _g1h->mark_evac_failure_object(_worker_id, old, word_sz); + _g1h->mark_evac_failure_object(old); _evacuation_failed_info.register_copy_failure(word_sz); @@ -730,8 +730,8 @@ G1ParScanThreadStateSet::G1ParScanThreadStateSet(G1CollectedHeap* g1h, G1ParScanThreadStateSet::~G1ParScanThreadStateSet() { assert(_flushed, "thread local state from the per thread states should have been flushed"); - FREE_C_HEAP_ARRAY(G1ParScanThreadState*, _states); - FREE_C_HEAP_ARRAY(size_t, _surviving_young_words_total); + FREE_C_HEAP_ARRAY(_states); + FREE_C_HEAP_ARRAY(_surviving_young_words_total); } #if TASKQUEUE_STATS diff --git a/src/hotspot/share/gc/g1/g1PeriodicGCTask.cpp b/src/hotspot/share/gc/g1/g1PeriodicGCTask.cpp index ee11bbd961f..b5ff4272764 100644 --- a/src/hotspot/share/gc/g1/g1PeriodicGCTask.cpp +++ b/src/hotspot/share/gc/g1/g1PeriodicGCTask.cpp @@ -23,6 +23,7 @@ */ #include "gc/g1/g1CollectedHeap.inline.hpp" +#include "gc/g1/g1CollectorState.inline.hpp" #include "gc/g1/g1ConcurrentMark.inline.hpp" #include "gc/g1/g1ConcurrentMarkThread.inline.hpp" #include "gc/g1/g1GCCounters.hpp" diff --git a/src/hotspot/share/gc/g1/g1Policy.cpp b/src/hotspot/share/gc/g1/g1Policy.cpp index dce83a3f084..78a533d62c0 100644 --- a/src/hotspot/share/gc/g1/g1Policy.cpp +++ b/src/hotspot/share/gc/g1/g1Policy.cpp @@ -28,6 +28,7 @@ #include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1CollectionSet.hpp" #include "gc/g1/g1CollectionSetCandidates.inline.hpp" +#include "gc/g1/g1CollectorState.inline.hpp" #include "gc/g1/g1ConcurrentMark.hpp" #include "gc/g1/g1ConcurrentMarkThread.inline.hpp" #include "gc/g1/g1ConcurrentRefine.hpp" @@ -689,11 +690,6 @@ void G1Policy::record_young_collection_start() { assert(_g1h->collection_set()->verify_young_ages(), "region age verification failed"); } -void G1Policy::record_concurrent_mark_init_end() { - assert(!collector_state()->initiate_conc_mark_if_possible(), "we should have cleared it by now"); - collector_state()->set_in_normal_young_gc(); -} - void G1Policy::record_concurrent_mark_remark_end() { double end_time_sec = os::elapsedTime(); double start_time_sec = cur_pause_start_sec(); @@ -729,7 +725,7 @@ double G1Policy::constant_other_time_ms(double pause_time_ms) const { } bool G1Policy::about_to_start_mixed_phase() const { - return collector_state()->is_in_concurrent_cycle() || collector_state()->is_in_young_gc_before_mixed(); + return collector_state()->is_in_concurrent_cycle() || collector_state()->is_in_prepare_mixed_gc(); } bool G1Policy::need_to_start_conc_mark(const char* source, size_t allocation_word_size) { @@ -737,15 +733,15 @@ bool G1Policy::need_to_start_conc_mark(const char* source, size_t allocation_wor return false; } - size_t marking_initiating_used_threshold = _ihop_control->get_conc_mark_start_threshold(); + size_t marking_initiating_old_gen_threshold = _ihop_control->old_gen_threshold_for_conc_mark_start(); size_t non_young_occupancy = _g1h->non_young_occupancy_after_allocation(allocation_word_size); bool result = false; - if (non_young_occupancy > marking_initiating_used_threshold) { + if (non_young_occupancy > marking_initiating_old_gen_threshold) { result = collector_state()->is_in_young_only_phase(); log_debug(gc, ergo, ihop)("%s non-young occupancy: %zuB allocation request: %zuB threshold: %zuB (%1.2f) source: %s", result ? "Request concurrent cycle initiation (occupancy higher than threshold)" : "Do not request concurrent cycle initiation (still doing mixed collections)", - non_young_occupancy, allocation_word_size * HeapWordSize, marking_initiating_used_threshold, (double) marking_initiating_used_threshold / _g1h->capacity() * 100, source); + non_young_occupancy, allocation_word_size * HeapWordSize, marking_initiating_old_gen_threshold, (double) marking_initiating_old_gen_threshold / _g1h->capacity() * 100, source); } return result; } @@ -794,7 +790,8 @@ void G1Policy::record_young_collection_end(bool concurrent_operation_is_full_mar bool is_young_only_pause = G1CollectorState::is_young_only_pause(this_pause); if (G1CollectorState::is_concurrent_start_pause(this_pause)) { - record_concurrent_mark_init_end(); + assert(!collector_state()->initiate_conc_mark_if_possible(), "we should have cleared it by now"); + collector_state()->set_in_normal_young_gc(); } else { maybe_start_marking(allocation_word_size); } @@ -935,7 +932,7 @@ void G1Policy::record_young_collection_end(bool concurrent_operation_is_full_mar record_pause(this_pause, start_time_sec, end_time_sec); - if (G1CollectorState::is_last_young_pause(this_pause)) { + if (G1CollectorState::is_prepare_mixed_pause(this_pause)) { assert(!G1CollectorState::is_concurrent_start_pause(this_pause), "The young GC before mixed is not allowed to be concurrent start GC"); // This has been the young GC before we start doing mixed GCs. We already @@ -965,14 +962,13 @@ void G1Policy::record_young_collection_end(bool concurrent_operation_is_full_mar _free_regions_at_end_of_collection = _g1h->num_free_regions(); + _old_gen_alloc_tracker.reset_after_gc(_g1h->humongous_regions_count() * G1HeapRegion::GrainBytes); // Do not update dynamic IHOP due to G1 periodic collection as it is highly likely // that in this case we are not running in a "normal" operating mode. if (_g1h->gc_cause() != GCCause::_g1_periodic_collection) { update_young_length_bounds(); - _old_gen_alloc_tracker.reset_after_gc(_g1h->humongous_regions_count() * G1HeapRegion::GrainBytes); - if (update_ihop_prediction(app_time_ms / 1000.0, - G1CollectorState::is_young_only_pause(this_pause))) { + if (update_ihop_prediction(app_time_ms / 1000.0, is_young_only_pause)) { _ihop_control->report_statistics(_g1h->gc_tracer_stw(), _g1h->non_young_occupancy_after_allocation(allocation_word_size)); } } else { @@ -1029,14 +1025,13 @@ bool G1Policy::update_ihop_prediction(double mutator_time_s, bool report = false; - double marking_to_mixed_time = -1.0; if (!this_gc_was_young_only && _concurrent_start_to_mixed.has_result()) { - marking_to_mixed_time = _concurrent_start_to_mixed.last_marking_time(); + double marking_to_mixed_time = _concurrent_start_to_mixed.get_and_reset_last_marking_time(); assert(marking_to_mixed_time > 0.0, "Concurrent start to mixed time must be larger than zero but is %.3f", marking_to_mixed_time); if (marking_to_mixed_time > min_valid_time) { - _ihop_control->update_marking_length(marking_to_mixed_time); + _ihop_control->add_marking_start_to_mixed_length(marking_to_mixed_time); report = true; } } @@ -1133,6 +1128,17 @@ double G1Policy::predict_eden_copy_time_ms(uint count, size_t* bytes_to_copy) co return _analytics->predict_object_copy_time_ms(expected_bytes, collector_state()->is_in_young_only_phase()); } +bool G1Policy::should_update_surv_rate_group_predictors() { + return collector_state()->is_in_young_only_phase() && !collector_state()->is_in_mark_or_rebuild(); +} + +void G1Policy::cset_regions_freed() { + bool update = should_update_surv_rate_group_predictors(); + + _eden_surv_rate_group->all_surviving_words_recorded(predictor(), update); + _survivor_surv_rate_group->all_surviving_words_recorded(predictor(), update); +} + double G1Policy::predict_region_copy_time_ms(G1HeapRegion* hr, bool for_young_only_phase) const { size_t const bytes_to_copy = predict_bytes_to_copy(hr); return _analytics->predict_object_copy_time_ms(bytes_to_copy, for_young_only_phase); @@ -1235,10 +1241,6 @@ bool G1Policy::force_concurrent_start_if_outside_cycle(GCCause::Cause gc_cause) } } -void G1Policy::initiate_conc_mark() { - collector_state()->set_in_concurrent_start_gc(); -} - static const char* requester_for_mixed_abort(GCCause::Cause cause) { if (cause == GCCause::_wb_breakpoint) { return "run_to breakpoint"; @@ -1254,7 +1256,7 @@ void G1Policy::decide_on_concurrent_start_pause() { // We are about to decide on whether this pause will be a // concurrent start pause. - // First, collector_state()->in_concurrent_start_gc() should not be already set. We + // First, collector_state()->is_in_concurrent_start_gc() should not already be set. We // will set it here if we have to. However, it should be cleared by // the end of the pause (it's only set for the duration of a // concurrent start pause). @@ -1273,22 +1275,19 @@ void G1Policy::decide_on_concurrent_start_pause() { log_debug(gc, ergo)("Do not initiate concurrent cycle (whitebox controlled)"); } else if (!about_to_start_mixed_phase() && collector_state()->is_in_young_only_phase()) { // Initiate a new concurrent start if there is no marking or reclamation going on. - initiate_conc_mark(); + collector_state()->set_in_concurrent_start_gc(); log_debug(gc, ergo)("Initiate concurrent cycle (concurrent cycle initiation requested)"); } else if (_g1h->is_user_requested_concurrent_full_gc(cause) || GCCause::is_codecache_requested_gc(cause) || (cause == GCCause::_wb_breakpoint)) { - // Initiate a concurrent start. A concurrent start must be a young only - // GC, so the collector state must be updated to reflect this. - collector_state()->set_in_normal_young_gc(); - + // Force concurrent start. + collector_state()->set_in_concurrent_start_gc(); // We might have ended up coming here about to start a mixed phase with a collection set // active. The following remark might change the change the "evacuation efficiency" of // the regions in this set, leading to failing asserts later. // Since the concurrent cycle will recreate the collection set anyway, simply drop it here. abandon_collection_set_candidates(); abort_time_to_mixed_tracking(); - initiate_conc_mark(); log_debug(gc, ergo)("Initiate concurrent cycle (%s requested concurrent cycle)", requester_for_mixed_abort(cause)); } else { @@ -1332,7 +1331,7 @@ void G1Policy::record_concurrent_mark_cleanup_end(bool has_rebuilt_remembered_se log_debug(gc, ergo)("request young-only gcs (candidate old regions not available)"); } if (mixed_gc_pending) { - collector_state()->set_in_young_gc_before_mixed(); + collector_state()->set_in_prepare_mixed_gc(); } double end_sec = os::elapsedTime(); @@ -1397,7 +1396,7 @@ void G1Policy::update_time_to_mixed_tracking(Pause gc_type, case Pause::Cleanup: case Pause::Remark: case Pause::Normal: - case Pause::LastYoung: + case Pause::PrepareMixed: _concurrent_start_to_mixed.add_pause(end - start); break; case Pause::ConcurrentStartFull: diff --git a/src/hotspot/share/gc/g1/g1Policy.hpp b/src/hotspot/share/gc/g1/g1Policy.hpp index 585e26441d5..5c5c2bc3572 100644 --- a/src/hotspot/share/gc/g1/g1Policy.hpp +++ b/src/hotspot/share/gc/g1/g1Policy.hpp @@ -114,9 +114,7 @@ class G1Policy: public CHeapObj { G1ConcurrentStartToMixedTimeTracker _concurrent_start_to_mixed; - bool should_update_surv_rate_group_predictors() { - return collector_state()->is_in_young_only_phase() && !collector_state()->is_in_mark_or_rebuild(); - } + bool should_update_surv_rate_group_predictors(); double pending_cards_processing_time() const; public: @@ -160,12 +158,7 @@ public: // bytes_to_copy is non-null. double predict_eden_copy_time_ms(uint count, size_t* bytes_to_copy = nullptr) const; - void cset_regions_freed() { - bool update = should_update_surv_rate_group_predictors(); - - _eden_surv_rate_group->all_surviving_words_recorded(predictor(), update); - _survivor_surv_rate_group->all_surviving_words_recorded(predictor(), update); - } + void cset_regions_freed(); G1MMUTracker* mmu_tracker() { return _mmu_tracker; @@ -320,9 +313,6 @@ public: void record_full_collection_start(); void record_full_collection_end(size_t allocation_word_size); - // Must currently be called while the world is stopped. - void record_concurrent_mark_init_end(); - void record_concurrent_mark_remark_end(); // Record start, end, and completion of cleanup. @@ -339,11 +329,6 @@ private: // regions and update the associated members. void update_survival_estimates_for_next_collection(); - // Set the state to start a concurrent marking cycle and clear - // _initiate_conc_mark_if_possible because it has now been - // acted on. - void initiate_conc_mark(); - public: // This sets the initiate_conc_mark_if_possible() flag to start a // new cycle, as long as we are not already in one. It's best if it diff --git a/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.cpp b/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.cpp index c5f55e1d20c..a9f4115df94 100644 --- a/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.cpp +++ b/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.cpp @@ -38,7 +38,7 @@ G1RegionMarkStatsCache::G1RegionMarkStatsCache(G1RegionMarkStats* target, uint n } G1RegionMarkStatsCache::~G1RegionMarkStatsCache() { - FREE_C_HEAP_ARRAY(G1RegionMarkStatsCacheEntry, _cache); + FREE_C_HEAP_ARRAY(_cache); } void G1RegionMarkStatsCache::add_live_words(oop obj) { diff --git a/src/hotspot/share/gc/g1/g1RegionsOnNodes.cpp b/src/hotspot/share/gc/g1/g1RegionsOnNodes.cpp index c1c0d471796..9550e57698e 100644 --- a/src/hotspot/share/gc/g1/g1RegionsOnNodes.cpp +++ b/src/hotspot/share/gc/g1/g1RegionsOnNodes.cpp @@ -32,7 +32,7 @@ G1RegionsOnNodes::G1RegionsOnNodes() : _count_per_node(nullptr), _numa(G1NUMA::n } G1RegionsOnNodes::~G1RegionsOnNodes() { - FREE_C_HEAP_ARRAY(uint, _count_per_node); + FREE_C_HEAP_ARRAY(_count_per_node); } void G1RegionsOnNodes::add(G1HeapRegion* hr) { diff --git a/src/hotspot/share/gc/g1/g1RemSet.cpp b/src/hotspot/share/gc/g1/g1RemSet.cpp index 4b4a8a68c30..be18a3065e9 100644 --- a/src/hotspot/share/gc/g1/g1RemSet.cpp +++ b/src/hotspot/share/gc/g1/g1RemSet.cpp @@ -31,6 +31,7 @@ #include "gc/g1/g1CardTableEntryClosure.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1CollectionSet.inline.hpp" +#include "gc/g1/g1CollectorState.inline.hpp" #include "gc/g1/g1ConcurrentRefine.hpp" #include "gc/g1/g1ConcurrentRefineSweepTask.hpp" #include "gc/g1/g1FromCardCache.hpp" @@ -123,8 +124,8 @@ class G1RemSetScanState : public CHeapObj { } ~G1DirtyRegions() { - FREE_C_HEAP_ARRAY(uint, _buffer); - FREE_C_HEAP_ARRAY(Atomic, _contains); + FREE_C_HEAP_ARRAY(_buffer); + FREE_C_HEAP_ARRAY(_contains); } void reset() { @@ -244,7 +245,7 @@ public: _scan_top(nullptr) { } ~G1RemSetScanState() { - FREE_C_HEAP_ARRAY(HeapWord*, _scan_top); + FREE_C_HEAP_ARRAY(_scan_top); } void initialize(uint max_reserved_regions) { diff --git a/src/hotspot/share/gc/g1/g1RemSetSummary.cpp b/src/hotspot/share/gc/g1/g1RemSetSummary.cpp index 3e9cf938097..1c0e15757cc 100644 --- a/src/hotspot/share/gc/g1/g1RemSetSummary.cpp +++ b/src/hotspot/share/gc/g1/g1RemSetSummary.cpp @@ -98,7 +98,7 @@ G1RemSetSummary::G1RemSetSummary(bool should_update) : } G1RemSetSummary::~G1RemSetSummary() { - FREE_C_HEAP_ARRAY(jlong, _worker_threads_cpu_times); + FREE_C_HEAP_ARRAY(_worker_threads_cpu_times); } void G1RemSetSummary::set(G1RemSetSummary* other) { diff --git a/src/hotspot/share/gc/g1/g1RootClosures.cpp b/src/hotspot/share/gc/g1/g1RootClosures.cpp index 2d5150c27aa..16c47cddea1 100644 --- a/src/hotspot/share/gc/g1/g1RootClosures.cpp +++ b/src/hotspot/share/gc/g1/g1RootClosures.cpp @@ -22,6 +22,7 @@ * */ +#include "gc/g1/g1CollectorState.inline.hpp" #include "gc/g1/g1OopClosures.inline.hpp" #include "gc/g1/g1RootClosures.hpp" #include "gc/g1/g1SharedClosures.hpp" diff --git a/src/hotspot/share/gc/g1/g1RootProcessor.cpp b/src/hotspot/share/gc/g1/g1RootProcessor.cpp index dac237cb277..a534eefb428 100644 --- a/src/hotspot/share/gc/g1/g1RootProcessor.cpp +++ b/src/hotspot/share/gc/g1/g1RootProcessor.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -27,7 +27,6 @@ #include "code/codeCache.hpp" #include "gc/g1/g1BarrierSet.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" -#include "gc/g1/g1CollectorState.hpp" #include "gc/g1/g1GCParPhaseTimesTracker.hpp" #include "gc/g1/g1GCPhaseTimes.hpp" #include "gc/g1/g1HeapRegion.inline.hpp" diff --git a/src/hotspot/share/gc/g1/g1SurvRateGroup.cpp b/src/hotspot/share/gc/g1/g1SurvRateGroup.cpp index f858b93b13d..15c6d3d1f15 100644 --- a/src/hotspot/share/gc/g1/g1SurvRateGroup.cpp +++ b/src/hotspot/share/gc/g1/g1SurvRateGroup.cpp @@ -65,8 +65,8 @@ void G1SurvRateGroup::start_adding_regions() { void G1SurvRateGroup::stop_adding_regions() { if (_num_added_regions > _stats_arrays_length) { - _accum_surv_rate_pred = REALLOC_C_HEAP_ARRAY(double, _accum_surv_rate_pred, _num_added_regions, mtGC); - _surv_rate_predictors = REALLOC_C_HEAP_ARRAY(TruncatedSeq*, _surv_rate_predictors, _num_added_regions, mtGC); + _accum_surv_rate_pred = REALLOC_C_HEAP_ARRAY(_accum_surv_rate_pred, _num_added_regions, mtGC); + _surv_rate_predictors = REALLOC_C_HEAP_ARRAY(_surv_rate_predictors, _num_added_regions, mtGC); for (uint i = _stats_arrays_length; i < _num_added_regions; ++i) { // Initialize predictors and accumulated survivor rate predictions. diff --git a/src/hotspot/share/gc/g1/g1Trace.cpp b/src/hotspot/share/gc/g1/g1Trace.cpp index 242a97ca4e3..d6eadda5d50 100644 --- a/src/hotspot/share/gc/g1/g1Trace.cpp +++ b/src/hotspot/share/gc/g1/g1Trace.cpp @@ -22,6 +22,7 @@ * */ +#include "gc/g1/g1CollectorState.inline.hpp" #include "gc/g1/g1EvacInfo.hpp" #include "gc/g1/g1HeapRegionTraceType.hpp" #include "gc/g1/g1Trace.hpp" diff --git a/src/hotspot/share/gc/g1/g1VMOperations.cpp b/src/hotspot/share/gc/g1/g1VMOperations.cpp index f98f0b078f3..891432e20a7 100644 --- a/src/hotspot/share/gc/g1/g1VMOperations.cpp +++ b/src/hotspot/share/gc/g1/g1VMOperations.cpp @@ -23,6 +23,7 @@ */ #include "gc/g1/g1CollectedHeap.inline.hpp" +#include "gc/g1/g1CollectorState.inline.hpp" #include "gc/g1/g1ConcurrentMarkThread.inline.hpp" #include "gc/g1/g1Policy.hpp" #include "gc/g1/g1Trace.hpp" @@ -151,8 +152,9 @@ bool VM_G1PauseConcurrent::doit_prologue() { G1CollectedHeap* g1h = G1CollectedHeap::heap(); if (g1h->is_shutting_down()) { Heap_lock->unlock(); - // JVM shutdown has started. This ensures that any further operations will be properly aborted - // and will not interfere with the shutdown process. + // JVM shutdown has started. Abort concurrent marking to ensure that any further + // concurrent VM operations will not try to start and interfere with the shutdown + // process. g1h->concurrent_mark()->abort_marking_threads(); return false; } diff --git a/src/hotspot/share/gc/g1/g1YoungCollector.cpp b/src/hotspot/share/gc/g1/g1YoungCollector.cpp index 04ccac5ff05..9c12127c864 100644 --- a/src/hotspot/share/gc/g1/g1YoungCollector.cpp +++ b/src/hotspot/share/gc/g1/g1YoungCollector.cpp @@ -31,7 +31,7 @@ #include "gc/g1/g1CardSetMemory.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1CollectionSetCandidates.inline.hpp" -#include "gc/g1/g1CollectorState.hpp" +#include "gc/g1/g1CollectorState.inline.hpp" #include "gc/g1/g1ConcurrentMark.hpp" #include "gc/g1/g1EvacFailureRegions.inline.hpp" #include "gc/g1/g1EvacInfo.hpp" @@ -244,19 +244,13 @@ G1YoungGCAllocationFailureInjector* G1YoungCollector::allocation_failure_injecto return _g1h->allocation_failure_injector(); } - -void G1YoungCollector::wait_for_root_region_scanning() { +void G1YoungCollector::complete_root_region_scan() { Ticks start = Ticks::now(); - // We have to wait until the CM threads finish scanning the - // root regions as it's the only way to ensure that all the - // objects on them have been correctly scanned before we start - // moving them during the GC. - bool waited = concurrent_mark()->wait_until_root_region_scan_finished(); - Tickspan wait_time; - if (waited) { - wait_time = (Ticks::now() - start); + // We have to complete root region scan as it's the only way to ensure that all the + // objects on them have been correctly scanned before we start moving them during the GC. + if (concurrent_mark()->complete_root_regions_scan_in_safepoint()) { + phase_times()->record_root_region_scan_time((Ticks::now() - start).seconds() * MILLIUNITS); } - phase_times()->record_root_region_scan_wait_time(wait_time.seconds() * MILLIUNITS); } class G1PrintCollectionSetClosure : public G1HeapRegionClosure { @@ -1147,7 +1141,7 @@ void G1YoungCollector::collect() { // Wait for root region scan here to make sure that it is done before any // use of the STW workers to maximize cpu use (i.e. all cores are available // just to do that). - wait_for_root_region_scanning(); + complete_root_region_scan(); G1YoungGCVerifierMark vm(this); { diff --git a/src/hotspot/share/gc/g1/g1YoungCollector.hpp b/src/hotspot/share/gc/g1/g1YoungCollector.hpp index 76d443b1a9f..ab32ca770a4 100644 --- a/src/hotspot/share/gc/g1/g1YoungCollector.hpp +++ b/src/hotspot/share/gc/g1/g1YoungCollector.hpp @@ -89,7 +89,7 @@ class G1YoungCollector { // returning the total time taken. Tickspan run_task_timed(WorkerTask* task); - void wait_for_root_region_scanning(); + void complete_root_region_scan(); void calculate_collection_set(G1EvacInfo* evacuation_info, double target_pause_time_ms); diff --git a/src/hotspot/share/gc/g1/g1YoungGCAllocationFailureInjector.cpp b/src/hotspot/share/gc/g1/g1YoungGCAllocationFailureInjector.cpp index 2291a755cd3..2b33a85da29 100644 --- a/src/hotspot/share/gc/g1/g1YoungGCAllocationFailureInjector.cpp +++ b/src/hotspot/share/gc/g1/g1YoungGCAllocationFailureInjector.cpp @@ -23,6 +23,7 @@ */ #include "gc/g1/g1CollectedHeap.inline.hpp" +#include "gc/g1/g1CollectorState.inline.hpp" #include "gc/g1/g1YoungGCAllocationFailureInjector.inline.hpp" #include "gc/shared/gc_globals.hpp" diff --git a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp index 3d49b8f025b..11da3cb8263 100644 --- a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp +++ b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp @@ -29,7 +29,7 @@ #include "gc/g1/g1CardTableEntryClosure.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1CollectionSetCandidates.inline.hpp" -#include "gc/g1/g1CollectorState.hpp" +#include "gc/g1/g1CollectorState.inline.hpp" #include "gc/g1/g1ConcurrentMark.inline.hpp" #include "gc/g1/g1EvacFailureRegions.inline.hpp" #include "gc/g1/g1EvacInfo.hpp" @@ -802,7 +802,7 @@ public: for (uint worker = 0; worker < _active_workers; worker++) { _worker_stats[worker].~FreeCSetStats(); } - FREE_C_HEAP_ARRAY(FreeCSetStats, _worker_stats); + FREE_C_HEAP_ARRAY(_worker_stats); _g1h->clear_collection_set(); diff --git a/src/hotspot/share/gc/g1/g1YoungGCPreEvacuateTasks.cpp b/src/hotspot/share/gc/g1/g1YoungGCPreEvacuateTasks.cpp index 936457659b6..7889fd4fb31 100644 --- a/src/hotspot/share/gc/g1/g1YoungGCPreEvacuateTasks.cpp +++ b/src/hotspot/share/gc/g1/g1YoungGCPreEvacuateTasks.cpp @@ -73,7 +73,7 @@ public: ~JavaThreadRetireTLABs() { static_assert(std::is_trivially_destructible::value, "must be"); - FREE_C_HEAP_ARRAY(ThreadLocalAllocStats, _local_tlab_stats); + FREE_C_HEAP_ARRAY(_local_tlab_stats); } void do_work(uint worker_id) override { diff --git a/src/hotspot/share/gc/g1/jvmFlagConstraintsG1.cpp b/src/hotspot/share/gc/g1/jvmFlagConstraintsG1.cpp index b56e82fac3c..df6adeb8041 100644 --- a/src/hotspot/share/gc/g1/jvmFlagConstraintsG1.cpp +++ b/src/hotspot/share/gc/g1/jvmFlagConstraintsG1.cpp @@ -70,7 +70,7 @@ JVMFlag::Error G1RemSetHowlMaxNumBucketsConstraintFunc(uint value, bool verbose) } if (!is_power_of_2(G1RemSetHowlMaxNumBuckets)) { JVMFlag::printError(verbose, - "G1RemSetMaxHowlNumBuckets (%u) must be a power of two.\n", + "G1RemSetHowlMaxNumBuckets (%u) must be a power of two.\n", value); return JVMFlag::VIOLATES_CONSTRAINT; } diff --git a/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp b/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp index c5d112ffbc1..8b514fe7199 100644 --- a/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp +++ b/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp @@ -55,7 +55,7 @@ MutableNUMASpace::MutableNUMASpace(size_t page_size) : MutableSpace(page_size) { lgrp_spaces()->append(new LGRPSpace(lgrp_ids[i], page_size)); } - FREE_C_HEAP_ARRAY(uint, lgrp_ids); + FREE_C_HEAP_ARRAY(lgrp_ids); } MutableNUMASpace::~MutableNUMASpace() { diff --git a/src/hotspot/share/gc/parallel/psCompactionManager.cpp b/src/hotspot/share/gc/parallel/psCompactionManager.cpp index 0108f1a9762..048355bfad3 100644 --- a/src/hotspot/share/gc/parallel/psCompactionManager.cpp +++ b/src/hotspot/share/gc/parallel/psCompactionManager.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, 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 @@ -58,7 +58,7 @@ PreservedMarksSet* ParCompactionManager::_preserved_marks_set = nullptr; ParCompactionManager::ParCompactionManager(PreservedMarks* preserved_marks, ReferenceProcessor* ref_processor, uint parallel_gc_threads) - :_partial_array_splitter(_partial_array_state_manager, parallel_gc_threads, ObjArrayMarkingStride), + :_partial_array_splitter(_partial_array_state_manager, parallel_gc_threads), _mark_and_push_closure(this, ref_processor) { ParallelScavengeHeap* heap = ParallelScavengeHeap::heap(); @@ -126,7 +126,7 @@ void ParCompactionManager::push_objArray(oop obj) { objArrayOop obj_array = objArrayOop(obj); size_t array_length = obj_array->length(); size_t initial_chunk_size = - _partial_array_splitter.start(&_marking_stack, obj_array, nullptr, array_length); + _partial_array_splitter.start(&_marking_stack, obj_array, nullptr, array_length, ObjArrayMarkingStride); follow_array(obj_array, 0, initial_chunk_size); } diff --git a/src/hotspot/share/gc/parallel/psParallelCompact.cpp b/src/hotspot/share/gc/parallel/psParallelCompact.cpp index d03bc3cda45..ca1fd2c120b 100644 --- a/src/hotspot/share/gc/parallel/psParallelCompact.cpp +++ b/src/hotspot/share/gc/parallel/psParallelCompact.cpp @@ -232,14 +232,10 @@ bool ParallelCompactData::initialize(MemRegion reserved_heap) assert(region_align_down(_heap_start) == _heap_start, "region start not aligned"); + assert(is_aligned(heap_size, RegionSize), "precondition"); - return initialize_region_data(heap_size); -} - -PSVirtualSpace* -ParallelCompactData::create_vspace(size_t count, size_t element_size) -{ - const size_t raw_bytes = count * element_size; + const size_t count = heap_size >> Log2RegionSize; + const size_t raw_bytes = count * sizeof(RegionData); const size_t page_sz = os::page_size_for_region_aligned(raw_bytes, 10); const size_t granularity = os::vm_allocation_granularity(); const size_t rs_align = MAX2(page_sz, granularity); @@ -253,7 +249,7 @@ ParallelCompactData::create_vspace(size_t count, size_t element_size) if (!rs.is_reserved()) { // Failed to reserve memory. - return nullptr; + return false; } os::trace_page_sizes("Parallel Compact Data", raw_bytes, raw_bytes, rs.base(), @@ -261,34 +257,23 @@ ParallelCompactData::create_vspace(size_t count, size_t element_size) MemTracker::record_virtual_memory_tag(rs, mtGC); - PSVirtualSpace* vspace = new PSVirtualSpace(rs, page_sz); + PSVirtualSpace* region_vspace = new PSVirtualSpace(rs, page_sz); - if (!vspace->expand_by(_reserved_byte_size)) { + if (!region_vspace->expand_by(_reserved_byte_size)) { // Failed to commit memory. - delete vspace; + delete region_vspace; // Release memory reserved in the space. MemoryReserver::release(rs); - return nullptr; + return false; } - return vspace; -} - -bool ParallelCompactData::initialize_region_data(size_t heap_size) -{ - assert(is_aligned(heap_size, RegionSize), "precondition"); - - const size_t count = heap_size >> Log2RegionSize; - _region_vspace = create_vspace(count, sizeof(RegionData)); - if (_region_vspace != nullptr) { - _region_data = (RegionData*)_region_vspace->reserved_low_addr(); - _region_count = count; - return true; - } - return false; + _region_vspace = region_vspace; + _region_data = (RegionData*)_region_vspace->reserved_low_addr(); + _region_count = count; + return true; } void ParallelCompactData::clear_range(size_t beg_region, size_t end_region) { diff --git a/src/hotspot/share/gc/parallel/psParallelCompact.hpp b/src/hotspot/share/gc/parallel/psParallelCompact.hpp index f5ab041fa97..25f4f66de6f 100644 --- a/src/hotspot/share/gc/parallel/psParallelCompact.hpp +++ b/src/hotspot/share/gc/parallel/psParallelCompact.hpp @@ -395,9 +395,6 @@ public: #endif // #ifdef ASSERT private: - bool initialize_region_data(size_t heap_size); - PSVirtualSpace* create_vspace(size_t count, size_t element_size); - HeapWord* _heap_start; #ifdef ASSERT HeapWord* _heap_end; diff --git a/src/hotspot/share/gc/parallel/psPromotionManager.cpp b/src/hotspot/share/gc/parallel/psPromotionManager.cpp index 39fcc5556c6..ac22430aa4c 100644 --- a/src/hotspot/share/gc/parallel/psPromotionManager.cpp +++ b/src/hotspot/share/gc/parallel/psPromotionManager.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2026, 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 @@ -158,7 +158,7 @@ PartialArrayTaskStats* PSPromotionManager::partial_array_task_stats() { // Most members are initialized either by initialize() or reset(). PSPromotionManager::PSPromotionManager() - : _partial_array_splitter(_partial_array_state_manager, ParallelGCThreads, ParGCArrayScanChunk) + : _partial_array_splitter(_partial_array_state_manager, ParallelGCThreads) { // We set the old lab's start array. _old_lab.set_start_array(old_gen()->start_array()); @@ -273,7 +273,7 @@ void PSPromotionManager::push_objArray(oop old_obj, oop new_obj) { size_t array_length = to_array->length(); size_t initial_chunk_size = // The source array is unused when processing states. - _partial_array_splitter.start(&_claimed_stack_depth, nullptr, to_array, array_length); + _partial_array_splitter.start(&_claimed_stack_depth, nullptr, to_array, array_length, ParGCArrayScanChunk); process_array_chunk(to_array, 0, initial_chunk_size); } diff --git a/src/hotspot/share/gc/shared/bufferNode.cpp b/src/hotspot/share/gc/shared/bufferNode.cpp index 90e50f52e84..855f872afab 100644 --- a/src/hotspot/share/gc/shared/bufferNode.cpp +++ b/src/hotspot/share/gc/shared/bufferNode.cpp @@ -41,7 +41,7 @@ void* BufferNode::AllocatorConfig::allocate() { void BufferNode::AllocatorConfig::deallocate(void* node) { assert(node != nullptr, "precondition"); - FREE_C_HEAP_ARRAY(char, node); + FREE_C_HEAP_ARRAY(node); } BufferNode::Allocator::Allocator(const char* name, size_t buffer_capacity) : diff --git a/src/hotspot/share/gc/shared/c1/barrierSetC1.cpp b/src/hotspot/share/gc/shared/c1/barrierSetC1.cpp index a31078f7e67..692893544d5 100644 --- a/src/hotspot/share/gc/shared/c1/barrierSetC1.cpp +++ b/src/hotspot/share/gc/shared/c1/barrierSetC1.cpp @@ -183,6 +183,7 @@ void BarrierSetC1::load_at_resolved(LIRAccess& access, LIR_Opr result) { bool needs_patching = (decorators & C1_NEEDS_PATCHING) != 0; bool mask_boolean = (decorators & C1_MASK_BOOLEAN) != 0; bool in_native = (decorators & IN_NATIVE) != 0; + bool needs_trailing_membar = is_volatile; if (support_IRIW_for_not_multiple_copy_atomic_cpu && is_volatile) { __ membar(); @@ -192,12 +193,15 @@ void BarrierSetC1::load_at_resolved(LIRAccess& access, LIR_Opr result) { if (in_native) { __ move_wide(access.resolved_addr()->as_address_ptr(), result); } else if ((is_volatile || needs_atomic) && !needs_patching) { + // volatile_field_load provides trailing membar semantics. + // Hence separate trailing membar is not needed. + needs_trailing_membar = false; 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); } - if (is_volatile) { + if (needs_trailing_membar) { __ membar_acquire(); } diff --git a/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp b/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp index a888ea81707..afe7d2acfa7 100644 --- a/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp +++ b/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp @@ -708,7 +708,6 @@ int BarrierSetC2::arraycopy_payload_base_offset(bool is_array) { // 12 - 64-bit VM, compressed klass // 16 - 64-bit VM, normal klass if (base_off % BytesPerLong != 0) { - assert(UseCompressedClassPointers, ""); assert(!UseCompactObjectHeaders, ""); if (is_array) { // Exclude length to copy by 8 bytes words. diff --git a/src/hotspot/share/gc/shared/classUnloadingContext.cpp b/src/hotspot/share/gc/shared/classUnloadingContext.cpp index 4eac2561e67..d2b4a2fc636 100644 --- a/src/hotspot/share/gc/shared/classUnloadingContext.cpp +++ b/src/hotspot/share/gc/shared/classUnloadingContext.cpp @@ -54,7 +54,7 @@ ClassUnloadingContext::~ClassUnloadingContext() { for (uint i = 0; i < _num_nmethod_unlink_workers; ++i) { delete _unlinked_nmethods[i]; } - FREE_C_HEAP_ARRAY(NMethodSet*, _unlinked_nmethods); + FREE_C_HEAP_ARRAY(_unlinked_nmethods); assert(_context == this, "context not set correctly"); _context = nullptr; diff --git a/src/hotspot/share/gc/shared/collectorCounters.cpp b/src/hotspot/share/gc/shared/collectorCounters.cpp index f01997f9854..1624c693470 100644 --- a/src/hotspot/share/gc/shared/collectorCounters.cpp +++ b/src/hotspot/share/gc/shared/collectorCounters.cpp @@ -62,7 +62,7 @@ CollectorCounters::CollectorCounters(const char* name, int ordinal) { } CollectorCounters::~CollectorCounters() { - FREE_C_HEAP_ARRAY(char, _name_space); + FREE_C_HEAP_ARRAY(_name_space); } TraceCollectorStats::TraceCollectorStats(CollectorCounters* c) : diff --git a/src/hotspot/share/gc/shared/concurrentGCThread.cpp b/src/hotspot/share/gc/shared/concurrentGCThread.cpp index ed6c1b4d283..c7765631cd9 100644 --- a/src/hotspot/share/gc/shared/concurrentGCThread.cpp +++ b/src/hotspot/share/gc/shared/concurrentGCThread.cpp @@ -33,9 +33,8 @@ ConcurrentGCThread::ConcurrentGCThread() : _should_terminate(false), _has_terminated(false) {} -void ConcurrentGCThread::create_and_start(ThreadPriority prio) { +void ConcurrentGCThread::create_and_start() { if (os::create_thread(this, os::gc_thread)) { - os::set_priority(this, prio); os::start_thread(this); } } diff --git a/src/hotspot/share/gc/shared/concurrentGCThread.hpp b/src/hotspot/share/gc/shared/concurrentGCThread.hpp index 0c764546045..5322d676493 100644 --- a/src/hotspot/share/gc/shared/concurrentGCThread.hpp +++ b/src/hotspot/share/gc/shared/concurrentGCThread.hpp @@ -36,7 +36,7 @@ private: Atomic _has_terminated; protected: - void create_and_start(ThreadPriority prio = NearMaxPriority); + void create_and_start(); virtual void run_service() = 0; virtual void stop_service() = 0; diff --git a/src/hotspot/share/gc/shared/gcTrace.cpp b/src/hotspot/share/gc/shared/gcTrace.cpp index bad9c707b1e..5d0627e779e 100644 --- a/src/hotspot/share/gc/shared/gcTrace.cpp +++ b/src/hotspot/share/gc/shared/gcTrace.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, 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 @@ -122,11 +122,10 @@ void GCTracer::report_gc_heap_summary(GCWhen::Type when, const GCHeapSummary& he void GCTracer::report_metaspace_summary(GCWhen::Type when, const MetaspaceSummary& summary) const { send_meta_space_summary_event(when, summary); - send_metaspace_chunk_free_list_summary(when, Metaspace::NonClassType, summary.metaspace_chunk_free_list_summary()); - if (UseCompressedClassPointers) { - send_metaspace_chunk_free_list_summary(when, Metaspace::ClassType, summary.class_chunk_free_list_summary()); - } +#if INCLUDE_CLASS_SPACE + send_metaspace_chunk_free_list_summary(when, Metaspace::ClassType, summary.class_chunk_free_list_summary()); +#endif } void YoungGCTracer::report_gc_end_impl(const Ticks& timestamp, TimePartitions* time_partitions) { diff --git a/src/hotspot/share/gc/shared/gc_globals.hpp b/src/hotspot/share/gc/shared/gc_globals.hpp index 66ca10f1fb6..c9102944197 100644 --- a/src/hotspot/share/gc/shared/gc_globals.hpp +++ b/src/hotspot/share/gc/shared/gc_globals.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -256,6 +256,12 @@ "before pushing a continuation entry") \ range(1, INT_MAX/2) \ \ + product(uintx, ArrayMarkingMinStride, 64, DIAGNOSTIC, \ + "Minimum chunk size for split array processing during marking; " \ + "the effective stride is clamped between this value " \ + "and ObjArrayMarkingStride.") \ + constraint(ArrayMarkingMinStrideConstraintFunc,AfterErgo) \ + \ product(bool, AggressiveHeap, false, \ "(Deprecated) Optimize heap options for long-running memory " \ "intensive apps") \ diff --git a/src/hotspot/share/gc/shared/generationCounters.cpp b/src/hotspot/share/gc/shared/generationCounters.cpp index f6bbc1c2618..85da4c99cbb 100644 --- a/src/hotspot/share/gc/shared/generationCounters.cpp +++ b/src/hotspot/share/gc/shared/generationCounters.cpp @@ -64,7 +64,7 @@ GenerationCounters::GenerationCounters(const char* name, } GenerationCounters::~GenerationCounters() { - FREE_C_HEAP_ARRAY(char, _name_space); + FREE_C_HEAP_ARRAY(_name_space); } void GenerationCounters::update_capacity(size_t curr_capacity) { diff --git a/src/hotspot/share/gc/shared/hSpaceCounters.cpp b/src/hotspot/share/gc/shared/hSpaceCounters.cpp index a873bc2f45c..5dd9d5bfaa8 100644 --- a/src/hotspot/share/gc/shared/hSpaceCounters.cpp +++ b/src/hotspot/share/gc/shared/hSpaceCounters.cpp @@ -66,7 +66,7 @@ HSpaceCounters::HSpaceCounters(const char* name_space, } HSpaceCounters::~HSpaceCounters() { - FREE_C_HEAP_ARRAY(char, _name_space); + FREE_C_HEAP_ARRAY(_name_space); } void HSpaceCounters::update_capacity(size_t v) { diff --git a/src/hotspot/share/gc/shared/jvmFlagConstraintsGC.cpp b/src/hotspot/share/gc/shared/jvmFlagConstraintsGC.cpp index ea3d644d105..4d7ffce3a5d 100644 --- a/src/hotspot/share/gc/shared/jvmFlagConstraintsGC.cpp +++ b/src/hotspot/share/gc/shared/jvmFlagConstraintsGC.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -414,3 +414,15 @@ JVMFlag::Error GCCardSizeInBytesConstraintFunc(uint value, bool verbose) { return JVMFlag::SUCCESS; } } + +JVMFlag::Error ArrayMarkingMinStrideConstraintFunc(uintx value, bool verbose) { + if (value > ObjArrayMarkingStride) { + JVMFlag::printError(verbose, + "ArrayMarkingMinStride (%zu) must be " + "less than or equal to ObjArrayMarkingStride (%zu)\n", + value, ObjArrayMarkingStride); + return JVMFlag::VIOLATES_CONSTRAINT; + } else { + return JVMFlag::SUCCESS; + } +} diff --git a/src/hotspot/share/gc/shared/jvmFlagConstraintsGC.hpp b/src/hotspot/share/gc/shared/jvmFlagConstraintsGC.hpp index a89f42959e1..1d2f45397aa 100644 --- a/src/hotspot/share/gc/shared/jvmFlagConstraintsGC.hpp +++ b/src/hotspot/share/gc/shared/jvmFlagConstraintsGC.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -66,7 +66,8 @@ f(uintx, SurvivorRatioConstraintFunc) \ f(size_t, MetaspaceSizeConstraintFunc) \ f(size_t, MaxMetaspaceSizeConstraintFunc) \ - f(uint, GCCardSizeInBytesConstraintFunc) + f(uint, GCCardSizeInBytesConstraintFunc) \ + f(uintx, ArrayMarkingMinStrideConstraintFunc) SHARED_GC_CONSTRAINTS(DECLARE_CONSTRAINT) diff --git a/src/hotspot/share/gc/shared/oopStorage.cpp b/src/hotspot/share/gc/shared/oopStorage.cpp index 21e63f6fc32..8aea565cdf7 100644 --- a/src/hotspot/share/gc/shared/oopStorage.cpp +++ b/src/hotspot/share/gc/shared/oopStorage.cpp @@ -136,7 +136,7 @@ OopStorage::ActiveArray* OopStorage::ActiveArray::create(size_t size, void OopStorage::ActiveArray::destroy(ActiveArray* ba) { ba->~ActiveArray(); - FREE_C_HEAP_ARRAY(char, ba); + FREE_C_HEAP_ARRAY(ba); } size_t OopStorage::ActiveArray::size() const { @@ -362,7 +362,7 @@ OopStorage::Block* OopStorage::Block::new_block(const OopStorage* owner) { void OopStorage::Block::delete_block(const Block& block) { void* memory = block._memory; block.Block::~Block(); - FREE_C_HEAP_ARRAY(char, memory); + FREE_C_HEAP_ARRAY(memory); } // This can return a false positive if ptr is not contained by some diff --git a/src/hotspot/share/gc/shared/partialArraySplitter.cpp b/src/hotspot/share/gc/shared/partialArraySplitter.cpp index d1833872683..04884d5e666 100644 --- a/src/hotspot/share/gc/shared/partialArraySplitter.cpp +++ b/src/hotspot/share/gc/shared/partialArraySplitter.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, 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 @@ -28,10 +28,9 @@ #include "utilities/macros.hpp" PartialArraySplitter::PartialArraySplitter(PartialArrayStateManager* manager, - uint num_workers, - size_t chunk_size) + uint num_workers) : _allocator(manager), - _stepper(num_workers, chunk_size) + _stepper(num_workers) TASKQUEUE_STATS_ONLY(COMMA _stats()) {} diff --git a/src/hotspot/share/gc/shared/partialArraySplitter.hpp b/src/hotspot/share/gc/shared/partialArraySplitter.hpp index 87cc137e797..340f370d1d5 100644 --- a/src/hotspot/share/gc/shared/partialArraySplitter.hpp +++ b/src/hotspot/share/gc/shared/partialArraySplitter.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, 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 @@ -44,8 +44,7 @@ class PartialArraySplitter { public: PartialArraySplitter(PartialArrayStateManager* manager, - uint num_workers, - size_t chunk_size); + uint num_workers); ~PartialArraySplitter() = default; NONCOPYABLE(PartialArraySplitter); @@ -60,6 +59,8 @@ public: // // length is their length in elements. // + // chunk_size the size of a single chunk. + // // If t is a ScannerTask, queue->push(t) must be a valid expression. The // result of that expression is ignored. // @@ -76,7 +77,8 @@ public: size_t start(Queue* queue, objArrayOop from_array, objArrayOop to_array, - size_t length); + size_t length, + size_t chunk_size); // Result type for claim(), carrying multiple values. Provides the claimed // chunk's start and end array indices. diff --git a/src/hotspot/share/gc/shared/partialArraySplitter.inline.hpp b/src/hotspot/share/gc/shared/partialArraySplitter.inline.hpp index abb0cf13101..7679358e218 100644 --- a/src/hotspot/share/gc/shared/partialArraySplitter.inline.hpp +++ b/src/hotspot/share/gc/shared/partialArraySplitter.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, 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 @@ -39,14 +39,16 @@ template size_t PartialArraySplitter::start(Queue* queue, objArrayOop source, objArrayOop destination, - size_t length) { - PartialArrayTaskStepper::Step step = _stepper.start(length); + size_t length, + size_t chunk_size) { + precond(chunk_size > 0); + PartialArrayTaskStepper::Step step = _stepper.start(length, chunk_size); // Push initial partial scan tasks. if (step._ncreate > 0) { TASKQUEUE_STATS_ONLY(_stats.inc_split();); TASKQUEUE_STATS_ONLY(_stats.inc_pushed(step._ncreate);) PartialArrayState* state = - _allocator.allocate(source, destination, step._index, length, step._ncreate); + _allocator.allocate(source, destination, step._index, length, chunk_size, step._ncreate); for (uint i = 0; i < step._ncreate; ++i) { queue->push(ScannerTask(state)); } @@ -75,9 +77,10 @@ PartialArraySplitter::claim(PartialArrayState* state, Queue* queue, bool stolen) queue->push(ScannerTask(state)); } } + size_t chunk_size = state->chunk_size(); // Release state, decrementing refcount, now that we're done with it. _allocator.release(state); - return Claim{step._index, step._index + _stepper.chunk_size()}; + return Claim{step._index, step._index + chunk_size}; } #endif // SHARE_GC_SHARED_PARTIALARRAYSPLITTER_INLINE_HPP diff --git a/src/hotspot/share/gc/shared/partialArrayState.cpp b/src/hotspot/share/gc/shared/partialArrayState.cpp index aadbc46b7c1..4679ac2f51c 100644 --- a/src/hotspot/share/gc/shared/partialArrayState.cpp +++ b/src/hotspot/share/gc/shared/partialArrayState.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, 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 @@ -35,10 +35,12 @@ PartialArrayState::PartialArrayState(oop src, oop dst, size_t index, size_t length, + size_t chunk_size, size_t initial_refcount) : _source(src), _destination(dst), _length(length), + _chunk_size(chunk_size), _index(index), _refcount(initial_refcount) { @@ -77,6 +79,7 @@ PartialArrayStateAllocator::~PartialArrayStateAllocator() { PartialArrayState* PartialArrayStateAllocator::allocate(oop src, oop dst, size_t index, size_t length, + size_t chunk_size, size_t initial_refcount) { void* p; FreeListEntry* head = _free_list; @@ -87,7 +90,7 @@ PartialArrayState* PartialArrayStateAllocator::allocate(oop src, oop dst, head->~FreeListEntry(); p = head; } - return ::new (p) PartialArrayState(src, dst, index, length, initial_refcount); + return ::new (p) PartialArrayState(src, dst, index, length, chunk_size, initial_refcount); } void PartialArrayStateAllocator::release(PartialArrayState* state) { @@ -111,7 +114,7 @@ PartialArrayStateManager::PartialArrayStateManager(uint max_allocators) PartialArrayStateManager::~PartialArrayStateManager() { reset(); - FREE_C_HEAP_ARRAY(Arena, _arenas); + FREE_C_HEAP_ARRAY(_arenas); } Arena* PartialArrayStateManager::register_allocator() { diff --git a/src/hotspot/share/gc/shared/partialArrayState.hpp b/src/hotspot/share/gc/shared/partialArrayState.hpp index 3dafeb0f14c..75e297526ae 100644 --- a/src/hotspot/share/gc/shared/partialArrayState.hpp +++ b/src/hotspot/share/gc/shared/partialArrayState.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, 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 @@ -61,6 +61,7 @@ class PartialArrayState { oop _source; oop _destination; size_t _length; + size_t _chunk_size; Atomic _index; Atomic _refcount; @@ -68,7 +69,7 @@ class PartialArrayState { PartialArrayState(oop src, oop dst, size_t index, size_t length, - size_t initial_refcount); + size_t chunk_size, size_t initial_refcount); public: // Deleted to require management by allocator object. @@ -89,6 +90,8 @@ public: // The length of the array oop. size_t length() const { return _length; } + size_t chunk_size() const { return _chunk_size; } + // A pointer to the start index for the next segment to process, for atomic // update. Atomic* index_addr() { return &_index; } @@ -130,6 +133,7 @@ public: // from the associated manager. PartialArrayState* allocate(oop src, oop dst, size_t index, size_t length, + size_t chunk_size, size_t initial_refcount); // Decrement the state's refcount. If the new refcount is zero, add the diff --git a/src/hotspot/share/gc/shared/partialArrayTaskStepper.cpp b/src/hotspot/share/gc/shared/partialArrayTaskStepper.cpp index d91ba347d6c..f7d53c9348a 100644 --- a/src/hotspot/share/gc/shared/partialArrayTaskStepper.cpp +++ b/src/hotspot/share/gc/shared/partialArrayTaskStepper.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -48,8 +48,7 @@ static uint compute_task_fanout(uint task_limit) { return result; } -PartialArrayTaskStepper::PartialArrayTaskStepper(uint n_workers, size_t chunk_size) : - _chunk_size(chunk_size), +PartialArrayTaskStepper::PartialArrayTaskStepper(uint n_workers) : _task_limit(compute_task_limit(n_workers)), _task_fanout(compute_task_fanout(_task_limit)) {} diff --git a/src/hotspot/share/gc/shared/partialArrayTaskStepper.hpp b/src/hotspot/share/gc/shared/partialArrayTaskStepper.hpp index 11499ca2ffe..594cc7b245a 100644 --- a/src/hotspot/share/gc/shared/partialArrayTaskStepper.hpp +++ b/src/hotspot/share/gc/shared/partialArrayTaskStepper.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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,19 +40,19 @@ class PartialArrayState; // substantially expand the task queues. class PartialArrayTaskStepper { public: - PartialArrayTaskStepper(uint n_workers, size_t chunk_size); + PartialArrayTaskStepper(uint n_workers); struct Step { size_t _index; // Array index for the step. uint _ncreate; // Number of new tasks to create. }; - // Called with the length of the array to be processed. Returns a Step with - // _index being the end of the initial chunk, which the caller should - // process. This is also the starting index for the next chunk to process. + // Called with the length of the array to be processed and chunk size. + // Returns a Step with _index being the end of the initial chunk, which the + // caller should process. This is also the starting index for the next chunk to process. // The _ncreate is the number of tasks to enqueue to continue processing the // array. If _ncreate is zero then _index will be length. - inline Step start(size_t length) const; + inline Step start(size_t length, size_t chunk_size) const; // Atomically increment state's index by chunk_size() to claim the next // chunk. Returns a Step with _index being the starting index of the @@ -60,21 +60,16 @@ public: // to enqueue. inline Step next(PartialArrayState* state) const; - // The size of chunks to claim for each task. - inline size_t chunk_size() const; - class TestSupport; // For unit tests private: - // Size (number of elements) of a chunk to process. - size_t _chunk_size; // Limit on the number of partial array tasks to create for a given array. uint _task_limit; // Maximum number of new tasks to create when processing an existing task. uint _task_fanout; // For unit tests. - inline Step next_impl(size_t length, Atomic* index_addr) const; + inline Step next_impl(size_t length, size_t chunk_size, Atomic* index_addr) const; }; #endif // SHARE_GC_SHARED_PARTIALARRAYTASKSTEPPER_HPP diff --git a/src/hotspot/share/gc/shared/partialArrayTaskStepper.inline.hpp b/src/hotspot/share/gc/shared/partialArrayTaskStepper.inline.hpp index 6946f7c69ff..538815698f2 100644 --- a/src/hotspot/share/gc/shared/partialArrayTaskStepper.inline.hpp +++ b/src/hotspot/share/gc/shared/partialArrayTaskStepper.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -31,13 +31,9 @@ #include "utilities/checkedCast.hpp" #include "utilities/debug.hpp" -size_t PartialArrayTaskStepper::chunk_size() const { - return _chunk_size; -} - PartialArrayTaskStepper::Step -PartialArrayTaskStepper::start(size_t length) const { - size_t end = length % _chunk_size; // End of initial chunk. +PartialArrayTaskStepper::start(size_t length, size_t chunk_size) const { + size_t end = length % chunk_size; // End of initial chunk. // If the initial chunk is the complete array, then don't need any partial // tasks. Otherwise, start with just one partial task; see new task // calculation in next(). @@ -45,24 +41,24 @@ PartialArrayTaskStepper::start(size_t length) const { } PartialArrayTaskStepper::Step -PartialArrayTaskStepper::next_impl(size_t length, Atomic* index_addr) const { +PartialArrayTaskStepper::next_impl(size_t length, size_t chunk_size, Atomic* index_addr) const { // The start of the next task is in the state's index. // Atomically increment by the chunk size to claim the associated chunk. // Because we limit the number of enqueued tasks to being no more than the // number of remaining chunks to process, we can use an atomic add for the // claim, rather than a CAS loop. - size_t start = index_addr->fetch_then_add(_chunk_size, memory_order_relaxed); + size_t start = index_addr->fetch_then_add(chunk_size, memory_order_relaxed); assert(start < length, "invariant: start %zu, length %zu", start, length); - assert(((length - start) % _chunk_size) == 0, + assert(((length - start) % chunk_size) == 0, "invariant: start %zu, length %zu, chunk size %zu", - start, length, _chunk_size); + start, length, chunk_size); // Determine the number of new tasks to create. // Zero-based index for this partial task. The initial task isn't counted. - uint task_num = checked_cast(start / _chunk_size); + uint task_num = checked_cast(start / chunk_size); // Number of tasks left to process, including this one. - uint remaining_tasks = checked_cast((length - start) / _chunk_size); + uint remaining_tasks = checked_cast((length - start) / chunk_size); assert(remaining_tasks > 0, "invariant"); // Compute number of pending tasks, including this one. The maximum number // of tasks is a function of task_num (N) and _task_fanout (F). @@ -89,7 +85,7 @@ PartialArrayTaskStepper::next_impl(size_t length, Atomic* index_addr) co PartialArrayTaskStepper::Step PartialArrayTaskStepper::next(PartialArrayState* state) const { - return next_impl(state->length(), state->index_addr()); + return next_impl(state->length(), state->chunk_size(), state->index_addr()); } #endif // SHARE_GC_SHARED_PARTIALARRAYTASKSTEPPER_INLINE_HPP diff --git a/src/hotspot/share/gc/shared/preservedMarks.cpp b/src/hotspot/share/gc/shared/preservedMarks.cpp index 605b7afe072..6a69fe10f95 100644 --- a/src/hotspot/share/gc/shared/preservedMarks.cpp +++ b/src/hotspot/share/gc/shared/preservedMarks.cpp @@ -148,7 +148,7 @@ void PreservedMarksSet::reclaim() { } if (_in_c_heap) { - FREE_C_HEAP_ARRAY(Padded, _stacks); + FREE_C_HEAP_ARRAY(_stacks); } else { // the array was resource-allocated, so nothing to do } diff --git a/src/hotspot/share/gc/shared/stringdedup/stringDedup.cpp b/src/hotspot/share/gc/shared/stringdedup/stringDedup.cpp index b3f96da1cce..ab1b15b0447 100644 --- a/src/hotspot/share/gc/shared/stringdedup/stringDedup.cpp +++ b/src/hotspot/share/gc/shared/stringdedup/stringDedup.cpp @@ -182,7 +182,7 @@ void StringDedup::Requests::flush() { assert(_storage_for_requests != nullptr, "invariant"); _storage_for_requests->storage()->release(_buffer, _index); } - FREE_C_HEAP_ARRAY(oop*, _buffer); + FREE_C_HEAP_ARRAY(_buffer); _buffer = nullptr; } if (_storage_for_requests != nullptr) { diff --git a/src/hotspot/share/gc/shared/stringdedup/stringDedupTable.cpp b/src/hotspot/share/gc/shared/stringdedup/stringDedupTable.cpp index a376f3b96de..546efa4774b 100644 --- a/src/hotspot/share/gc/shared/stringdedup/stringDedupTable.cpp +++ b/src/hotspot/share/gc/shared/stringdedup/stringDedupTable.cpp @@ -455,7 +455,7 @@ void StringDedup::Table::free_buckets(Bucket* buckets, size_t number_of_buckets) while (number_of_buckets > 0) { buckets[--number_of_buckets].~Bucket(); } - FREE_C_HEAP_ARRAY(Bucket, buckets); + FREE_C_HEAP_ARRAY(buckets); } // Compute the hash code for obj using halfsiphash_32. As this is a high diff --git a/src/hotspot/share/gc/shared/taskqueue.inline.hpp b/src/hotspot/share/gc/shared/taskqueue.inline.hpp index e77645f4fcf..c942b6cc3e9 100644 --- a/src/hotspot/share/gc/shared/taskqueue.inline.hpp +++ b/src/hotspot/share/gc/shared/taskqueue.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -48,7 +48,7 @@ inline GenericTaskQueueSet::GenericTaskQueueSet(uint n) : _n(n) { template inline GenericTaskQueueSet::~GenericTaskQueueSet() { - FREE_C_HEAP_ARRAY(T*, _queues); + FREE_C_HEAP_ARRAY(_queues); } #if TASKQUEUE_STATS @@ -279,13 +279,11 @@ typename GenericTaskQueue::PopResult GenericTaskQueue::pop_g // Increment top; if it wraps, also increment tag, to distinguish it // from any recent _age for the same top() index. idx_t new_top = increment_index(oldAge.top()); + // Don't use bottom, since a pop_local might have decremented it. + assert_not_underflow(localBot, new_top); idx_t new_tag = oldAge.tag() + ((new_top == 0) ? 1 : 0); Age newAge(new_top, new_tag); bool result = par_set_age(oldAge, newAge); - - // Note that using "bottom" here might fail, since a pop_local might - // have decremented it. - assert_not_underflow(localBot, newAge.top()); return result ? PopResult::Success : PopResult::Contended; } diff --git a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp index 4c160929f5a..f9b8694eb04 100644 --- a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp +++ b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp @@ -37,7 +37,7 @@ #include "utilities/copy.hpp" size_t ThreadLocalAllocBuffer::_max_size = 0; -unsigned int ThreadLocalAllocBuffer::_target_refills = 0; +unsigned int ThreadLocalAllocBuffer::_target_num_refills = 0; ThreadLocalAllocBuffer::ThreadLocalAllocBuffer() : _start(nullptr), @@ -48,10 +48,10 @@ ThreadLocalAllocBuffer::ThreadLocalAllocBuffer() : _desired_size(0), _refill_waste_limit(0), _allocated_before_last_gc(0), - _number_of_refills(0), + _num_refills(0), _refill_waste(0), _gc_waste(0), - _slow_allocations(0), + _num_slow_allocations(0), _allocated_size(0), _allocation_fraction(TLABAllocationWeight) { @@ -81,7 +81,7 @@ void ThreadLocalAllocBuffer::accumulate_and_reset_statistics(ThreadLocalAllocSta print_stats("gc"); - if (_number_of_refills > 0) { + if (_num_refills > 0) { // Update allocation history if a reasonable amount of eden was allocated. bool update_allocation_history = used > 0.5 * capacity; @@ -98,16 +98,16 @@ void ThreadLocalAllocBuffer::accumulate_and_reset_statistics(ThreadLocalAllocSta _allocation_fraction.sample(alloc_frac); } - stats->update_fast_allocations(_number_of_refills, + stats->update_fast_allocations(_num_refills, _allocated_size, _gc_waste, _refill_waste); } else { - assert(_number_of_refills == 0 && _refill_waste == 0 && _gc_waste == 0, + assert(_num_refills == 0 && _refill_waste == 0 && _gc_waste == 0, "tlab stats == 0"); } - stats->update_slow_allocations(_slow_allocations); + stats->update_num_slow_allocations(_num_slow_allocations); reset_statistics(); } @@ -147,7 +147,7 @@ void ThreadLocalAllocBuffer::resize() { assert(ResizeTLAB, "Should not call this otherwise"); size_t alloc = (size_t)(_allocation_fraction.average() * (Universe::heap()->tlab_capacity() / HeapWordSize)); - size_t new_size = alloc / _target_refills; + size_t new_size = alloc / _target_num_refills; new_size = clamp(new_size, min_size(), max_size()); @@ -156,24 +156,24 @@ void ThreadLocalAllocBuffer::resize() { log_trace(gc, tlab)("TLAB new size: thread: " PTR_FORMAT " [id: %2d]" " refills %d alloc: %8.6f desired_size: %zu -> %zu", p2i(thread()), thread()->osthread()->thread_id(), - _target_refills, _allocation_fraction.average(), desired_size(), aligned_new_size); + _target_num_refills, _allocation_fraction.average(), desired_size(), aligned_new_size); set_desired_size(aligned_new_size); set_refill_waste_limit(initial_refill_waste_limit()); } void ThreadLocalAllocBuffer::reset_statistics() { - _number_of_refills = 0; - _refill_waste = 0; - _gc_waste = 0; - _slow_allocations = 0; - _allocated_size = 0; + _num_refills = 0; + _refill_waste = 0; + _gc_waste = 0; + _num_slow_allocations = 0; + _allocated_size = 0; } void ThreadLocalAllocBuffer::fill(HeapWord* start, HeapWord* top, size_t new_size) { - _number_of_refills++; + _num_refills++; _allocated_size += new_size; print_stats("fill"); assert(top <= start + new_size - alignment_reserve(), "size too small"); @@ -205,7 +205,7 @@ void ThreadLocalAllocBuffer::initialize() { size_t capacity = Universe::heap()->tlab_capacity() / HeapWordSize; if (capacity > 0) { // Keep alloc_frac as float and not double to avoid the double to float conversion - float alloc_frac = desired_size() * target_refills() / (float)capacity; + float alloc_frac = desired_size() * target_num_refills() / (float)capacity; _allocation_fraction.sample(alloc_frac); } @@ -219,10 +219,10 @@ void ThreadLocalAllocBuffer::startup_initialization() { // Assuming each thread's active tlab is, on average, // 1/2 full at a GC - _target_refills = 100 / (2 * TLABWasteTargetPercent); - // We need to set initial target refills to 2 to avoid a GC which causes VM + _target_num_refills = 100 / (2 * TLABWasteTargetPercent); + // We need to set the initial target number of refills to 2 to avoid a GC which causes VM // abort during VM initialization. - _target_refills = MAX2(_target_refills, 2U); + _target_num_refills = MAX2(_target_num_refills, 2U); // During jvm startup, the main thread is initialized // before the heap is initialized. So reinitialize it now. @@ -240,10 +240,10 @@ size_t ThreadLocalAllocBuffer::initial_desired_size() { init_sz = TLABSize / HeapWordSize; } else { // Initial size is a function of the average number of allocating threads. - unsigned int nof_threads = ThreadLocalAllocStats::allocating_threads_avg(); + unsigned int num_threads = ThreadLocalAllocStats::num_allocating_threads_avg(); init_sz = (Universe::heap()->tlab_capacity() / HeapWordSize) / - (nof_threads * target_refills()); + (num_threads * target_num_refills()); init_sz = align_object_size(init_sz); } // We can't use clamp() between min_size() and max_size() here because some @@ -271,10 +271,10 @@ void ThreadLocalAllocBuffer::print_stats(const char* tag) { " slow: %dB", tag, p2i(thrd), thrd->osthread()->thread_id(), _desired_size / (K / HeapWordSize), - _slow_allocations, _refill_waste_limit * HeapWordSize, + _num_slow_allocations, _refill_waste_limit * HeapWordSize, _allocation_fraction.average(), _allocation_fraction.average() * tlab_used / K, - _number_of_refills, waste_percent, + _num_refills, waste_percent, _gc_waste * HeapWordSize, _refill_waste * HeapWordSize); } @@ -299,17 +299,17 @@ HeapWord* ThreadLocalAllocBuffer::hard_end() { return _allocation_end + alignment_reserve(); } -PerfVariable* ThreadLocalAllocStats::_perf_allocating_threads; -PerfVariable* ThreadLocalAllocStats::_perf_total_refills; -PerfVariable* ThreadLocalAllocStats::_perf_max_refills; -PerfVariable* ThreadLocalAllocStats::_perf_total_allocations; +PerfVariable* ThreadLocalAllocStats::_perf_num_allocating_threads; +PerfVariable* ThreadLocalAllocStats::_perf_total_num_refills; +PerfVariable* ThreadLocalAllocStats::_perf_max_num_refills; +PerfVariable* ThreadLocalAllocStats::_perf_total_allocated_size; PerfVariable* ThreadLocalAllocStats::_perf_total_gc_waste; PerfVariable* ThreadLocalAllocStats::_perf_max_gc_waste; PerfVariable* ThreadLocalAllocStats::_perf_total_refill_waste; PerfVariable* ThreadLocalAllocStats::_perf_max_refill_waste; -PerfVariable* ThreadLocalAllocStats::_perf_total_slow_allocations; -PerfVariable* ThreadLocalAllocStats::_perf_max_slow_allocations; -AdaptiveWeightedAverage ThreadLocalAllocStats::_allocating_threads_avg(0); +PerfVariable* ThreadLocalAllocStats::_perf_total_num_slow_allocations; +PerfVariable* ThreadLocalAllocStats::_perf_max_num_slow_allocations; +AdaptiveWeightedAverage ThreadLocalAllocStats::_num_allocating_threads_avg(0); static PerfVariable* create_perf_variable(const char* name, PerfData::Units unit, TRAPS) { ResourceMark rm; @@ -317,114 +317,114 @@ static PerfVariable* create_perf_variable(const char* name, PerfData::Units unit } void ThreadLocalAllocStats::initialize() { - _allocating_threads_avg = AdaptiveWeightedAverage(TLABAllocationWeight); - _allocating_threads_avg.sample(1); // One allocating thread at startup + _num_allocating_threads_avg = AdaptiveWeightedAverage(TLABAllocationWeight); + _num_allocating_threads_avg.sample(1); // One allocating thread at startup if (UsePerfData) { EXCEPTION_MARK; - _perf_allocating_threads = create_perf_variable("allocThreads", PerfData::U_None, CHECK); - _perf_total_refills = create_perf_variable("fills", PerfData::U_None, CHECK); - _perf_max_refills = create_perf_variable("maxFills", PerfData::U_None, CHECK); - _perf_total_allocations = create_perf_variable("alloc", PerfData::U_Bytes, CHECK); - _perf_total_gc_waste = create_perf_variable("gcWaste", PerfData::U_Bytes, CHECK); - _perf_max_gc_waste = create_perf_variable("maxGcWaste", PerfData::U_Bytes, CHECK); - _perf_total_refill_waste = create_perf_variable("refillWaste", PerfData::U_Bytes, CHECK); - _perf_max_refill_waste = create_perf_variable("maxRefillWaste", PerfData::U_Bytes, CHECK); - _perf_total_slow_allocations = create_perf_variable("slowAlloc", PerfData::U_None, CHECK); - _perf_max_slow_allocations = create_perf_variable("maxSlowAlloc", PerfData::U_None, CHECK); + _perf_num_allocating_threads = create_perf_variable("allocThreads", PerfData::U_None, CHECK); + _perf_total_num_refills = create_perf_variable("fills", PerfData::U_None, CHECK); + _perf_max_num_refills = create_perf_variable("maxFills", PerfData::U_None, CHECK); + _perf_total_allocated_size = create_perf_variable("alloc", PerfData::U_Bytes, CHECK); + _perf_total_gc_waste = create_perf_variable("gcWaste", PerfData::U_Bytes, CHECK); + _perf_max_gc_waste = create_perf_variable("maxGcWaste", PerfData::U_Bytes, CHECK); + _perf_total_refill_waste = create_perf_variable("refillWaste", PerfData::U_Bytes, CHECK); + _perf_max_refill_waste = create_perf_variable("maxRefillWaste", PerfData::U_Bytes, CHECK); + _perf_total_num_slow_allocations = create_perf_variable("slowAlloc", PerfData::U_None, CHECK); + _perf_max_num_slow_allocations = create_perf_variable("maxSlowAlloc", PerfData::U_None, CHECK); } } ThreadLocalAllocStats::ThreadLocalAllocStats() : - _allocating_threads(0), - _total_refills(0), - _max_refills(0), - _total_allocations(0), + _num_allocating_threads(0), + _total_num_refills(0), + _max_num_refills(0), + _total_allocated_size(0), _total_gc_waste(0), _max_gc_waste(0), _total_refill_waste(0), _max_refill_waste(0), - _total_slow_allocations(0), - _max_slow_allocations(0) {} + _total_num_slow_allocations(0), + _max_num_slow_allocations(0) {} -unsigned int ThreadLocalAllocStats::allocating_threads_avg() { - return MAX2((unsigned int)(_allocating_threads_avg.average() + 0.5), 1U); +unsigned int ThreadLocalAllocStats::num_allocating_threads_avg() { + return MAX2((unsigned int)(_num_allocating_threads_avg.average() + 0.5), 1U); } -void ThreadLocalAllocStats::update_fast_allocations(unsigned int refills, - size_t allocations, - size_t gc_waste, - size_t refill_waste) { - _allocating_threads += 1; - _total_refills += refills; - _max_refills = MAX2(_max_refills, refills); - _total_allocations += allocations; +void ThreadLocalAllocStats::update_fast_allocations(unsigned int num_refills, + size_t allocated_size, + size_t gc_waste, + size_t refill_waste) { + _num_allocating_threads += 1; + _total_num_refills += num_refills; + _max_num_refills = MAX2(_max_num_refills, num_refills); + _total_allocated_size += allocated_size; _total_gc_waste += gc_waste; _max_gc_waste = MAX2(_max_gc_waste, gc_waste); _total_refill_waste += refill_waste; _max_refill_waste = MAX2(_max_refill_waste, refill_waste); } -void ThreadLocalAllocStats::update_slow_allocations(unsigned int allocations) { - _total_slow_allocations += allocations; - _max_slow_allocations = MAX2(_max_slow_allocations, allocations); +void ThreadLocalAllocStats::update_num_slow_allocations(unsigned int num_slow_allocations) { + _total_num_slow_allocations += num_slow_allocations; + _max_num_slow_allocations = MAX2(_max_num_slow_allocations, num_slow_allocations); } void ThreadLocalAllocStats::update(const ThreadLocalAllocStats& other) { - _allocating_threads += other._allocating_threads; - _total_refills += other._total_refills; - _max_refills = MAX2(_max_refills, other._max_refills); - _total_allocations += other._total_allocations; - _total_gc_waste += other._total_gc_waste; - _max_gc_waste = MAX2(_max_gc_waste, other._max_gc_waste); - _total_refill_waste += other._total_refill_waste; - _max_refill_waste = MAX2(_max_refill_waste, other._max_refill_waste); - _total_slow_allocations += other._total_slow_allocations; - _max_slow_allocations = MAX2(_max_slow_allocations, other._max_slow_allocations); + _num_allocating_threads += other._num_allocating_threads; + _total_num_refills += other._total_num_refills; + _max_num_refills = MAX2(_max_num_refills, other._max_num_refills); + _total_allocated_size += other._total_allocated_size; + _total_gc_waste += other._total_gc_waste; + _max_gc_waste = MAX2(_max_gc_waste, other._max_gc_waste); + _total_refill_waste += other._total_refill_waste; + _max_refill_waste = MAX2(_max_refill_waste, other._max_refill_waste); + _total_num_slow_allocations += other._total_num_slow_allocations; + _max_num_slow_allocations = MAX2(_max_num_slow_allocations, other._max_num_slow_allocations); } void ThreadLocalAllocStats::reset() { - _allocating_threads = 0; - _total_refills = 0; - _max_refills = 0; - _total_allocations = 0; - _total_gc_waste = 0; - _max_gc_waste = 0; - _total_refill_waste = 0; - _max_refill_waste = 0; - _total_slow_allocations = 0; - _max_slow_allocations = 0; + _num_allocating_threads = 0; + _total_num_refills = 0; + _max_num_refills = 0; + _total_allocated_size = 0; + _total_gc_waste = 0; + _max_gc_waste = 0; + _total_refill_waste = 0; + _max_refill_waste = 0; + _total_num_slow_allocations = 0; + _max_num_slow_allocations = 0; } void ThreadLocalAllocStats::publish() { - if (_total_allocations == 0) { + if (_total_allocated_size == 0) { return; } - _allocating_threads_avg.sample(_allocating_threads); + _num_allocating_threads_avg.sample(_num_allocating_threads); const size_t waste = _total_gc_waste + _total_refill_waste; - const double waste_percent = percent_of(waste, _total_allocations); + const double waste_percent = percent_of(waste, _total_allocated_size); log_debug(gc, tlab)("TLAB totals: thrds: %d refills: %d max: %d" " slow allocs: %d max %d waste: %4.1f%%" " gc: %zuB max: %zuB" " slow: %zuB max: %zuB", - _allocating_threads, _total_refills, _max_refills, - _total_slow_allocations, _max_slow_allocations, waste_percent, + _num_allocating_threads, _total_num_refills, _max_num_refills, + _total_num_slow_allocations, _max_num_slow_allocations, waste_percent, _total_gc_waste * HeapWordSize, _max_gc_waste * HeapWordSize, _total_refill_waste * HeapWordSize, _max_refill_waste * HeapWordSize); if (UsePerfData) { - _perf_allocating_threads ->set_value(_allocating_threads); - _perf_total_refills ->set_value(_total_refills); - _perf_max_refills ->set_value(_max_refills); - _perf_total_allocations ->set_value(_total_allocations); - _perf_total_gc_waste ->set_value(_total_gc_waste); - _perf_max_gc_waste ->set_value(_max_gc_waste); - _perf_total_refill_waste ->set_value(_total_refill_waste); - _perf_max_refill_waste ->set_value(_max_refill_waste); - _perf_total_slow_allocations ->set_value(_total_slow_allocations); - _perf_max_slow_allocations ->set_value(_max_slow_allocations); + _perf_num_allocating_threads ->set_value(_num_allocating_threads); + _perf_total_num_refills ->set_value(_total_num_refills); + _perf_max_num_refills ->set_value(_max_num_refills); + _perf_total_allocated_size ->set_value(_total_allocated_size); + _perf_total_gc_waste ->set_value(_total_gc_waste); + _perf_max_gc_waste ->set_value(_max_gc_waste); + _perf_total_refill_waste ->set_value(_total_refill_waste); + _perf_max_refill_waste ->set_value(_max_refill_waste); + _perf_total_num_slow_allocations ->set_value(_total_num_slow_allocations); + _perf_max_num_slow_allocations ->set_value(_max_num_slow_allocations); } } diff --git a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp index eb664d13961..67bc149013e 100644 --- a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp +++ b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp @@ -56,13 +56,13 @@ private: size_t _refill_waste_limit; // hold onto tlab if free() is larger than this uint64_t _allocated_before_last_gc; // total bytes allocated up until the last gc - static size_t _max_size; // maximum size of any TLAB - static unsigned _target_refills; // expected number of refills between GCs + static size_t _max_size; // maximum size of any TLAB + static unsigned _target_num_refills; // expected number of refills between GCs - unsigned _number_of_refills; + unsigned _num_refills; unsigned _refill_waste; unsigned _gc_waste; - unsigned _slow_allocations; + unsigned _num_slow_allocations; size_t _allocated_size; AdaptiveWeightedAverage _allocation_fraction; // fraction of eden allocated in tlabs @@ -79,7 +79,7 @@ private: size_t initial_refill_waste_limit(); - static int target_refills() { return _target_refills; } + static int target_num_refills() { return _target_num_refills; } size_t initial_desired_size(); size_t remaining(); @@ -98,9 +98,9 @@ private: // statistics - int number_of_refills() const { return _number_of_refills; } - int gc_waste() const { return _gc_waste; } - int slow_allocations() const { return _slow_allocations; } + int num_refills() const { return _num_refills; } + int gc_waste() const { return _gc_waste; } + int num_slow_allocations() const { return _num_slow_allocations; } public: ThreadLocalAllocBuffer(); @@ -179,41 +179,41 @@ public: class ThreadLocalAllocStats : public StackObj { private: - static PerfVariable* _perf_allocating_threads; - static PerfVariable* _perf_total_refills; - static PerfVariable* _perf_max_refills; - static PerfVariable* _perf_total_allocations; + static PerfVariable* _perf_num_allocating_threads; + static PerfVariable* _perf_total_num_refills; + static PerfVariable* _perf_max_num_refills; + static PerfVariable* _perf_total_allocated_size; static PerfVariable* _perf_total_gc_waste; static PerfVariable* _perf_max_gc_waste; static PerfVariable* _perf_total_refill_waste; static PerfVariable* _perf_max_refill_waste; - static PerfVariable* _perf_total_slow_allocations; - static PerfVariable* _perf_max_slow_allocations; + static PerfVariable* _perf_total_num_slow_allocations; + static PerfVariable* _perf_max_num_slow_allocations; - static AdaptiveWeightedAverage _allocating_threads_avg; + static AdaptiveWeightedAverage _num_allocating_threads_avg; - unsigned int _allocating_threads; - unsigned int _total_refills; - unsigned int _max_refills; - size_t _total_allocations; + unsigned int _num_allocating_threads; + unsigned int _total_num_refills; + unsigned int _max_num_refills; + size_t _total_allocated_size; size_t _total_gc_waste; size_t _max_gc_waste; size_t _total_refill_waste; size_t _max_refill_waste; - unsigned int _total_slow_allocations; - unsigned int _max_slow_allocations; + unsigned int _total_num_slow_allocations; + unsigned int _max_num_slow_allocations; public: static void initialize(); - static unsigned int allocating_threads_avg(); + static unsigned int num_allocating_threads_avg(); ThreadLocalAllocStats(); - void update_fast_allocations(unsigned int refills, - size_t allocations, + void update_fast_allocations(unsigned int num_refills, + size_t allocated_size, size_t gc_waste, size_t refill_waste); - void update_slow_allocations(unsigned int allocations); + void update_num_slow_allocations(unsigned int num_slow_allocations); void update(const ThreadLocalAllocStats& other); void reset(); diff --git a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.inline.hpp b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.inline.hpp index 441686c5c4c..727467f98d0 100644 --- a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.inline.hpp +++ b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.inline.hpp @@ -82,7 +82,7 @@ void ThreadLocalAllocBuffer::record_slow_allocation(size_t obj_size) { set_refill_waste_limit(refill_waste_limit() + refill_waste_limit_increment()); - _slow_allocations++; + _num_slow_allocations++; log_develop_trace(gc, tlab)("TLAB: %s thread: " PTR_FORMAT " [id: %2d]" " obj: %zu" diff --git a/src/hotspot/share/gc/shared/workerDataArray.inline.hpp b/src/hotspot/share/gc/shared/workerDataArray.inline.hpp index 34549bc079e..3b7658ec4c8 100644 --- a/src/hotspot/share/gc/shared/workerDataArray.inline.hpp +++ b/src/hotspot/share/gc/shared/workerDataArray.inline.hpp @@ -72,7 +72,7 @@ WorkerDataArray::~WorkerDataArray() { for (uint i = 0; i < MaxThreadWorkItems; i++) { delete _thread_work_items[i]; } - FREE_C_HEAP_ARRAY(T, _data); + FREE_C_HEAP_ARRAY(_data); } template diff --git a/src/hotspot/share/gc/shared/workerThread.cpp b/src/hotspot/share/gc/shared/workerThread.cpp index 2f6f003608f..2738c98e5c3 100644 --- a/src/hotspot/share/gc/shared/workerThread.cpp +++ b/src/hotspot/share/gc/shared/workerThread.cpp @@ -210,8 +210,6 @@ WorkerThread::WorkerThread(const char* name_prefix, uint name_suffix, WorkerTask } void WorkerThread::run() { - os::set_priority(this, NearMaxPriority); - while (true) { _dispatcher->worker_run_task(); } diff --git a/src/hotspot/share/gc/shared/workerUtils.cpp b/src/hotspot/share/gc/shared/workerUtils.cpp index 1826b9d7df8..736a9b007dd 100644 --- a/src/hotspot/share/gc/shared/workerUtils.cpp +++ b/src/hotspot/share/gc/shared/workerUtils.cpp @@ -122,7 +122,7 @@ bool SubTasksDone::try_claim_task(uint t) { SubTasksDone::~SubTasksDone() { assert(_verification_done.load_relaxed(), "all_tasks_claimed must have been called."); - FREE_C_HEAP_ARRAY(Atomic, _tasks); + FREE_C_HEAP_ARRAY(_tasks); } // *** SequentialSubTasksDone diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp index 6b327d7e4af..7a2be24f84c 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp @@ -111,12 +111,12 @@ ShenandoahAdaptiveHeuristics::ShenandoahAdaptiveHeuristics(ShenandoahSpaceInfo* } ShenandoahAdaptiveHeuristics::~ShenandoahAdaptiveHeuristics() { - FREE_C_HEAP_ARRAY(double, _spike_acceleration_rate_samples); - FREE_C_HEAP_ARRAY(double, _spike_acceleration_rate_timestamps); - FREE_C_HEAP_ARRAY(double, _gc_time_timestamps); - FREE_C_HEAP_ARRAY(double, _gc_time_samples); - FREE_C_HEAP_ARRAY(double, _gc_time_xy); - FREE_C_HEAP_ARRAY(double, _gc_time_xx); + FREE_C_HEAP_ARRAY(_spike_acceleration_rate_samples); + FREE_C_HEAP_ARRAY(_spike_acceleration_rate_timestamps); + FREE_C_HEAP_ARRAY(_gc_time_timestamps); + FREE_C_HEAP_ARRAY(_gc_time_samples); + FREE_C_HEAP_ARRAY(_gc_time_xy); + FREE_C_HEAP_ARRAY(_gc_time_xx); } void ShenandoahAdaptiveHeuristics::initialize() { @@ -209,14 +209,14 @@ void ShenandoahAdaptiveHeuristics::choose_collection_set_from_regiondata(Shenand } } -void ShenandoahAdaptiveHeuristics::add_degenerated_gc_time(double timestamp, double gc_time) { +void ShenandoahAdaptiveHeuristics::add_degenerated_gc_time(double time_at_start, double gc_time) { // Conservatively add sample into linear model If this time is above the predicted concurrent gc time - if (predict_gc_time(timestamp) < gc_time) { - add_gc_time(timestamp, gc_time); + if (predict_gc_time(time_at_start) < gc_time) { + add_gc_time(time_at_start, gc_time); } } -void ShenandoahAdaptiveHeuristics::add_gc_time(double timestamp, double gc_time) { +void ShenandoahAdaptiveHeuristics::add_gc_time(double time_at_start, double gc_time) { // Update best-fit linear predictor of GC time uint index = (_gc_time_first_sample_index + _gc_time_num_samples) % GC_TIME_SAMPLE_SIZE; if (_gc_time_num_samples == GC_TIME_SAMPLE_SIZE) { @@ -225,10 +225,10 @@ void ShenandoahAdaptiveHeuristics::add_gc_time(double timestamp, double gc_time) _gc_time_sum_of_xy -= _gc_time_xy[index]; _gc_time_sum_of_xx -= _gc_time_xx[index]; } - _gc_time_timestamps[index] = timestamp; + _gc_time_timestamps[index] = time_at_start; _gc_time_samples[index] = gc_time; - _gc_time_xy[index] = timestamp * gc_time; - _gc_time_xx[index] = timestamp * timestamp; + _gc_time_xy[index] = time_at_start * gc_time; + _gc_time_xx[index] = time_at_start * time_at_start; _gc_time_sum_of_timestamps += _gc_time_timestamps[index]; _gc_time_sum_of_samples += _gc_time_samples[index]; @@ -247,18 +247,26 @@ void ShenandoahAdaptiveHeuristics::add_gc_time(double timestamp, double gc_time) _gc_time_b = gc_time; _gc_time_sd = 0.0; } else if (_gc_time_num_samples == 2) { - // Two points define a line - double delta_y = gc_time - _gc_time_samples[_gc_time_first_sample_index]; - double delta_x = timestamp - _gc_time_timestamps[_gc_time_first_sample_index]; - _gc_time_m = delta_y / delta_x; + assert(time_at_start > _gc_time_timestamps[_gc_time_first_sample_index], + "Two GC cycles cannot finish at same time: %.6f vs %.6f, with GC times %.6f and %.6f", time_at_start, + _gc_time_timestamps[_gc_time_first_sample_index], gc_time, _gc_time_samples[_gc_time_first_sample_index]); + + // Two points define a line + double delta_x = time_at_start - _gc_time_timestamps[_gc_time_first_sample_index]; + double delta_y = gc_time - _gc_time_samples[_gc_time_first_sample_index]; + _gc_time_m = delta_y / delta_x; // y = mx + b // so b = y0 - mx0 - _gc_time_b = gc_time - _gc_time_m * timestamp; + _gc_time_b = gc_time - _gc_time_m * time_at_start; _gc_time_sd = 0.0; } else { + // Since timestamps are monotonically increasing, denominator does not equal zero. + double denominator = _gc_time_num_samples * _gc_time_sum_of_xx - _gc_time_sum_of_timestamps * _gc_time_sum_of_timestamps; + assert(denominator != 0.0, "Invariant: samples: %u, sum_of_xx: %.6f, sum_of_timestamps: %.6f", + _gc_time_num_samples, _gc_time_sum_of_xx, _gc_time_sum_of_timestamps); _gc_time_m = ((_gc_time_num_samples * _gc_time_sum_of_xy - _gc_time_sum_of_timestamps * _gc_time_sum_of_samples) / - (_gc_time_num_samples * _gc_time_sum_of_xx - _gc_time_sum_of_timestamps * _gc_time_sum_of_timestamps)); + denominator); _gc_time_b = (_gc_time_sum_of_samples - _gc_time_m * _gc_time_sum_of_timestamps) / _gc_time_num_samples; double sum_of_squared_deviations = 0.0; for (size_t i = 0; i < _gc_time_num_samples; i++) { diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp index 08d628e67ff..594367e2972 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp @@ -38,11 +38,6 @@ using idx_t = ShenandoahSimpleBitMap::idx_t; -typedef struct { - ShenandoahHeapRegion* _region; - size_t _live_data; -} AgedRegionData; - static int compare_by_aged_live(AgedRegionData a, AgedRegionData b) { if (a._live_data < b._live_data) return -1; @@ -74,17 +69,29 @@ ShenandoahGenerationalHeuristics::ShenandoahGenerationalHeuristics(ShenandoahGen : ShenandoahAdaptiveHeuristics(generation), _generation(generation), _add_regions_to_old(0) { } -void ShenandoahGenerationalHeuristics::choose_collection_set(ShenandoahCollectionSet* collection_set) { +void ShenandoahGenerationalHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* collection_set, + RegionData* data, size_t data_size, + size_t free) { ShenandoahGenerationalHeap* heap = ShenandoahGenerationalHeap::heap(); - assert(collection_set->is_empty(), "Collection set must be empty here"); - _add_regions_to_old = 0; - // Choose the collection set - filter_regions(collection_set); + // Find the amount that will be promoted, regions that will be promoted in + // place, and preselected older regions that will be promoted by evacuation. + ShenandoahInPlacePromotionPlanner in_place_promotions(heap); + compute_evacuation_budgets(in_place_promotions, heap); - if (!collection_set->is_empty() && _generation->is_global()) { + // Call the subclasses to add regions into the collection set. + select_collection_set_regions(collection_set, data, data_size, free); + + // Even if collection_set->is_empty(), we want to adjust budgets, making reserves available to mutator. + adjust_evacuation_budgets(heap, collection_set); + + if (collection_set->has_old_regions()) { + heap->shenandoah_policy()->record_mixed_cycle(); + } + + if (_generation->is_global()) { // We have just chosen a collection set for a global cycle. The mark bitmap covering old regions is complete, so // the remembered set scan can use that to avoid walking into garbage. When the next old mark begins, we will // use the mark bitmap to make the old regions parsable by coalescing and filling any unmarked objects. Thus, @@ -94,8 +101,16 @@ void ShenandoahGenerationalHeuristics::choose_collection_set(ShenandoahCollectio // coalesce those regions. Only the old regions which are not part of the collection set at this point are // eligible for coalescing. As implemented now, this has the side effect of possibly initiating mixed-evacuations // after a global cycle for old regions that were not included in this collection set. - heap->old_generation()->prepare_for_mixed_collections_after_global_gc(); + heap->old_generation()->transition_old_generation_after_global_gc(); } + + ShenandoahTracer::report_promotion_info(collection_set, + in_place_promotions.humongous_region_stats().count, + in_place_promotions.humongous_region_stats().garbage, + in_place_promotions.humongous_region_stats().free, + in_place_promotions.regular_region_stats().count, + in_place_promotions.regular_region_stats().garbage, + in_place_promotions.regular_region_stats().free); } void ShenandoahGenerationalHeuristics::compute_evacuation_budgets(ShenandoahInPlacePromotionPlanner& in_place_promotions, @@ -221,112 +236,48 @@ void ShenandoahGenerationalHeuristics::compute_evacuation_budgets(ShenandoahInPl // case of a GLOBAL gc. During choose_collection_set() of GLOBAL, old will be expanded on demand. } -void ShenandoahGenerationalHeuristics::filter_regions(ShenandoahCollectionSet* collection_set) { - auto heap = ShenandoahGenerationalHeap::heap(); - const size_t region_size_bytes = ShenandoahHeapRegion::region_size_bytes(); +void ShenandoahGenerationalHeuristics::add_tenured_regions_to_collection_set(const size_t old_promotion_reserve, + ShenandoahGenerationalHeap *const heap, + size_t candidates, AgedRegionData* sorted_regions) { + size_t old_consumed = 0; + if (candidates > 0) { + // Sort in increasing order according to live data bytes. Note that + // candidates represents the number of regions that qualify to be promoted + // by evacuation. + QuickSort::sort(sorted_regions, candidates, + compare_by_aged_live); - // Check all pinned regions have updated status before choosing the collection set. - heap->assert_pinned_region_status(_generation); - - // Step 1. Build up the region candidates we care about, rejecting losers and accepting winners right away. - - const size_t num_regions = heap->num_regions(); - - RegionData* candidates = _region_data; - - size_t cand_idx = 0; - - size_t total_garbage = 0; - - size_t immediate_garbage = 0; - size_t immediate_regions = 0; - - size_t free = 0; - size_t free_regions = 0; - - for (size_t i = 0; i < num_regions; i++) { - ShenandoahHeapRegion* region = heap->get_region(i); - if (!_generation->contains(region)) { - continue; - } - const size_t garbage = region->garbage(); - total_garbage += garbage; - if (region->is_empty()) { - free_regions++; - free += region_size_bytes; - } else if (region->is_regular()) { - if (!region->has_live()) { - // We can recycle it right away and put it in the free set. - immediate_regions++; - immediate_garbage += garbage; - region->make_trash_immediate(); - } else { - // This is our candidate for later consideration. Note that this region - // could still be promoted in place and may not necessarily end up in the - // collection set. - assert(region->get_top_before_promote() == nullptr, "Cannot add region %zu scheduled for in-place-promotion to the collection set", i); - candidates[cand_idx].set_region_and_garbage(region, garbage); - cand_idx++; + size_t selected_regions = 0; + size_t selected_live = 0; + for (size_t i = 0; i < candidates; i++) { + ShenandoahHeapRegion *const region = sorted_regions[i]._region; + const size_t region_live_data = sorted_regions[i]._live_data; + const size_t promotion_need = (size_t)(region_live_data * ShenandoahPromoEvacWaste); + if (old_consumed + promotion_need > old_promotion_reserve) { + // We rejected the remaining promotable regions from the collection set + // because we have no room to hold their evacuees. We do not need to + // iterate the remaining regions to estimate the amount we expect to + // promote because we know it directly form the census we computed + // during the preceding mark phase. + break; } - } else if (region->is_humongous_start()) { - // Reclaim humongous regions here, and count them as the immediate garbage - DEBUG_ONLY(assert_humongous_mark_consistency(region)); - if (!region->has_live()) { - heap->trash_humongous_region_at(region); - // Count only the start. Continuations would be counted on "trash" path - immediate_regions++; - immediate_garbage += garbage; - } - } else if (region->is_trash()) { - // Count in just trashed humongous continuation regions - immediate_regions++; - immediate_garbage += garbage; + old_consumed += promotion_need; + heap->collection_set()->add_region(region); + selected_regions++; + selected_live += region_live_data; } + log_debug(gc, ergo)( "Preselected %zu regions containing " PROPERFMT " live data," + " consuming: " PROPERFMT " of budgeted: " PROPERFMT, + selected_regions, PROPERFMTARGS(selected_live), + PROPERFMTARGS(old_consumed), PROPERFMTARGS(old_promotion_reserve)); } - - // Step 2. Look back at garbage statistics, and decide if we want to collect anything, - // given the amount of immediately reclaimable garbage. If we do, figure out the collection set. - assert(immediate_garbage <= total_garbage, - "Cannot have more immediate garbage than total garbage: " PROPERFMT " vs " PROPERFMT, - PROPERFMTARGS(immediate_garbage), PROPERFMTARGS(total_garbage)); - - const size_t immediate_percent = (total_garbage == 0) ? 0 : (immediate_garbage * 100 / total_garbage); - ShenandoahInPlacePromotionPlanner in_place_promotions(heap); - if (immediate_percent <= ShenandoahImmediateThreshold) { - - // Find the amount that will be promoted, regions that will be promoted in - // place, and preselected older regions that will be promoted by evacuation. - compute_evacuation_budgets(in_place_promotions, heap); - - // Call the subclasses to add young-gen regions into the collection set. - choose_collection_set_from_regiondata(collection_set, candidates, cand_idx, immediate_garbage + free); - - // Even if collection_set->is_empty(), we want to adjust budgets, making reserves available to mutator. - adjust_evacuation_budgets(heap, collection_set); - - if (collection_set->has_old_regions()) { - heap->shenandoah_policy()->record_mixed_cycle(); - } - } - - collection_set->summarize(total_garbage, immediate_garbage, immediate_regions); - ShenandoahTracer::report_evacuation_info(collection_set, - free_regions, - in_place_promotions.humongous_region_stats().count, - in_place_promotions.regular_region_stats().count, - in_place_promotions.regular_region_stats().garbage, - in_place_promotions.regular_region_stats().free, - immediate_regions, - immediate_garbage); } -// Select for inclusion into the collection set all regions whose age is at or above tenure age and for which the -// garbage percentage exceeds a dynamically adjusted threshold (known as the old-garbage threshold percentage). We -// identify these regions by setting the appropriate entry of the collection set's preselected regions array to true. -// All entries are initialized to false before calling this function. +// Select for inclusion into the collection set all regions whose age is at or +// above tenure age and for which the +// garbage percentage exceeds a dynamically adjusted threshold (known as the old-garbage threshold percentage). // -// During the subsequent selection of the collection set, we give priority to these promotion set candidates. // Without this prioritization, we found that the aged regions tend to be ignored because they typically have // much less garbage and much more live data than the recently allocated "eden" regions. When aged regions are // repeatedly excluded from the collection set, the amount of live memory within the young generation tends to @@ -334,8 +285,8 @@ void ShenandoahGenerationalHeuristics::filter_regions(ShenandoahCollectionSet* c // CPU and wall-clock time. // // A second benefit of treating aged regions differently than other regions during collection set selection is -// that this allows us to more accurately budget memory to hold the results of evacuation. Memory for evacuation -// of aged regions must be reserved in the old generation. Memory for evacuation of all other regions must be +// that this allows us to more accurately budget memory to hold the results of evacuation. Memory for evacuation +// of aged regions must be reserved in the old generation. Memory for evacuation of all other regions must be // reserved in the young generation. size_t ShenandoahGenerationalHeuristics::select_aged_regions(ShenandoahInPlacePromotionPlanner& in_place_promotions, const size_t old_promotion_reserve) { @@ -345,7 +296,6 @@ size_t ShenandoahGenerationalHeuristics::select_aged_regions(ShenandoahInPlacePr auto const heap = ShenandoahGenerationalHeap::heap(); - size_t promo_potential = 0; size_t candidates = 0; // Sort the promotion-eligible regions in order of increasing live-data-bytes so that we can first reclaim regions that require @@ -386,66 +336,28 @@ size_t ShenandoahGenerationalHeuristics::select_aged_regions(ShenandoahInPlacePr sorted_regions[candidates]._live_data = r->get_live_data_bytes(); candidates++; } - } else { - // We only evacuate & promote objects from regular regions whose garbage() is above old-garbage-threshold. - // Objects in tenure-worthy regions with less garbage are promoted in place. These take a different path to - // old-gen. Regions excluded from promotion because their garbage content is too low (causing us to anticipate that - // the region would be promoted in place) may be eligible for evacuation promotion by the time promotion takes - // place during a subsequent GC pass because more garbage is found within the region between now and then. This - // should not happen if we are properly adapting the tenure age. The theory behind adaptive tenuring threshold - // is to choose the youngest age that demonstrates no "significant" further loss of population since the previous - // age. If not this, we expect the tenure age to demonstrate linear population decay for at least two population - // samples, whereas we expect to observe exponential population decay for ages younger than the tenure age. - // - // In the case that certain regions which were anticipated to be promoted in place need to be promoted by - // evacuation, it may be the case that there is not sufficient reserve within old-gen to hold evacuation of - // these regions. The likely outcome is that these regions will not be selected for evacuation or promotion - // in the current cycle and we will anticipate that they will be promoted in the next cycle. This will cause - // us to reserve more old-gen memory so that these objects can be promoted in the subsequent cycle. - if (heap->is_aging_cycle() && heap->age_census()->is_tenurable(r->age() + 1)) { - if (r->garbage() >= in_place_promotions.old_garbage_threshold()) { - promo_potential += r->get_live_data_bytes(); - } - } } - // Note that we keep going even if one region is excluded from selection. - // Subsequent regions may be selected if they have smaller live data. } in_place_promotions.complete_planning(); - // Sort in increasing order according to live data bytes. Note that candidates represents the number of regions - // that qualify to be promoted by evacuation. - size_t old_consumed = 0; - if (candidates > 0) { - size_t selected_regions = 0; - size_t selected_live = 0; - QuickSort::sort(sorted_regions, candidates, compare_by_aged_live); - for (size_t i = 0; i < candidates; i++) { - ShenandoahHeapRegion* const region = sorted_regions[i]._region; - const size_t region_live_data = sorted_regions[i]._live_data; - const size_t promotion_need = (size_t) (region_live_data * ShenandoahPromoEvacWaste); - if (old_consumed + promotion_need <= old_promotion_reserve) { - old_consumed += promotion_need; - heap->collection_set()->add_region(region); - selected_regions++; - selected_live += region_live_data; - } else { - // We rejected this promotable region from the collection set because we had no room to hold its copy. - // Add this region to promo potential for next GC. - promo_potential += region_live_data; - assert(!heap->collection_set()->is_in(region), "Region %zu shouldn't be in the collection set", region->index()); - } - // We keep going even if one region is excluded from selection because we need to accumulate all eligible - // regions that are not preselected into promo_potential - } - log_debug(gc, ergo)("Preselected %zu regions containing " PROPERFMT " live data," - " consuming: " PROPERFMT " of budgeted: " PROPERFMT, - selected_regions, PROPERFMTARGS(selected_live), PROPERFMTARGS(old_consumed), PROPERFMTARGS(old_promotion_reserve)); - } + add_tenured_regions_to_collection_set(old_promotion_reserve, heap, candidates, sorted_regions); - log_info(gc, ergo)("Promotion potential of aged regions with sufficient garbage: " PROPERFMT, PROPERFMTARGS(promo_potential)); + const uint tenuring_threshold = heap->age_census()->tenuring_threshold(); + const size_t tenurable_this_cycle = heap->age_census()->get_tenurable_bytes(tenuring_threshold); + const size_t tenurable_next_cycle = heap->age_census()->get_tenurable_bytes(tenuring_threshold - 1); + assert(tenurable_next_cycle >= tenurable_this_cycle, + "Tenurable next cycle (" PROPERFMT ") should include tenurable this cycle (" PROPERFMT ")", + PROPERFMTARGS(tenurable_next_cycle), PROPERFMTARGS(tenurable_this_cycle)); + + const size_t max_promotions = tenurable_this_cycle * ShenandoahPromoEvacWaste; + const size_t old_consumed = MIN2(max_promotions, old_promotion_reserve); + + // Don't include the bytes we expect to promote in this cycle in the next cycle + const size_t promo_potential = (tenurable_next_cycle - tenurable_this_cycle) * ShenandoahPromoEvacWaste; heap->old_generation()->set_promotion_potential(promo_potential); + log_info(gc, ergo)("Promotion potential of aged regions with sufficient garbage: " PROPERFMT, PROPERFMTARGS(promo_potential)); + return old_consumed; } @@ -498,7 +410,11 @@ void ShenandoahGenerationalHeuristics::adjust_evacuation_budgets(ShenandoahHeap* size_t young_evacuated = collection_set->get_live_bytes_in_untenurable_regions(); size_t young_evacuated_reserve_used = (size_t) (ShenandoahEvacWaste * double(young_evacuated)); + // In top_off_collection_set(), we shrunk planned future reserve by _add_regions_to_old * region_size_bytes, but we + // didn't shrink available. The current reserve is not affected by the planned future reserve. Current available is + // larger than planned available by the planned adjustment amount. size_t total_young_available = young_generation->available_with_reserve() - _add_regions_to_old * region_size_bytes; + assert(young_evacuated_reserve_used <= total_young_available, "Cannot evacuate (%zu) more than is available in young (%zu)", young_evacuated_reserve_used, total_young_available); young_generation->set_evacuation_reserve(young_evacuated_reserve_used); diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp index c418e8c24ec..8ea5cdb36c8 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp @@ -34,6 +34,11 @@ class ShenandoahHeap; class ShenandoahCollectionSet; class RegionData; +typedef struct { + ShenandoahHeapRegion* _region; + size_t _live_data; +} AgedRegionData; + /* * This class serves as the base class for heuristics used to trigger and * choose the collection sets for young and global collections. It leans @@ -48,9 +53,12 @@ class ShenandoahGenerationalHeuristics : public ShenandoahAdaptiveHeuristics { public: explicit ShenandoahGenerationalHeuristics(ShenandoahGeneration* generation); - void choose_collection_set(ShenandoahCollectionSet* collection_set) override; + void post_initialize() override; - virtual void post_initialize() override; + // Wraps budget computation, subclass region selection, budget adjustment, and tracing. + void choose_collection_set_from_regiondata(ShenandoahCollectionSet* set, + RegionData* data, size_t data_size, + size_t free) override; private: // Compute evacuation budgets prior to choosing collection set. @@ -73,15 +81,23 @@ private: // to false. size_t select_aged_regions(ShenandoahInPlacePromotionPlanner& in_place_promotions, const size_t old_promotion_reserve); - // Filter and sort remaining regions before adding to collection set. - void filter_regions(ShenandoahCollectionSet* collection_set); + // Select regions for inclusion in the collection set that are tenured, but do + // not hold enough live data to warrant promotion in place. + void add_tenured_regions_to_collection_set(size_t old_promotion_reserve, + ShenandoahGenerationalHeap *const heap, + size_t candidates, AgedRegionData* sorted_regions); - // Adjust evacuation budgets after choosing collection set. The argument regions_to_xfer + // Adjust evacuation budgets after choosing collection set. On entry, the instance variable _regions_to_xfer // represents regions to be transferred to old based on decisions made in top_off_collection_set() void adjust_evacuation_budgets(ShenandoahHeap* const heap, ShenandoahCollectionSet* const collection_set); protected: + // Subclasses override this to perform generation-specific region selection. + virtual void select_collection_set_regions(ShenandoahCollectionSet* set, + RegionData* data, size_t data_size, + size_t free) = 0; + ShenandoahGeneration* _generation; size_t _add_regions_to_old; diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.cpp index ed25cd2e1a9..9452e8b28cb 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.cpp @@ -112,9 +112,9 @@ ShenandoahGlobalHeuristics::ShenandoahGlobalHeuristics(ShenandoahGlobalGeneratio : ShenandoahGenerationalHeuristics(generation) { } -void ShenandoahGlobalHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, - RegionData* data, size_t size, - size_t actual_free) { +void ShenandoahGlobalHeuristics::select_collection_set_regions(ShenandoahCollectionSet* cset, + RegionData* data, size_t size, + size_t actual_free) { QuickSort::sort(data, size, compare_by_garbage); choose_global_collection_set(cset, data, size, actual_free, 0); } diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.hpp index 8102fa24d14..1e96a665704 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.hpp @@ -161,9 +161,9 @@ class ShenandoahGlobalHeuristics : public ShenandoahGenerationalHeuristics { public: ShenandoahGlobalHeuristics(ShenandoahGlobalGeneration* generation); - void choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, - RegionData* data, size_t size, - size_t actual_free) override; + void select_collection_set_regions(ShenandoahCollectionSet* cset, + RegionData* data, size_t size, + size_t actual_free) override; private: void choose_global_collection_set(ShenandoahCollectionSet* cset, diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp index 895088381ee..d2010d921b1 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp @@ -29,6 +29,7 @@ #include "gc/shenandoah/shenandoahCollectorPolicy.hpp" #include "gc/shenandoah/shenandoahHeapRegion.inline.hpp" #include "gc/shenandoah/shenandoahMarkingContext.inline.hpp" +#include "gc/shenandoah/shenandoahTrace.hpp" #include "logging/log.hpp" #include "logging/logTag.hpp" #include "runtime/globals_extension.hpp" @@ -72,17 +73,13 @@ ShenandoahHeuristics::ShenandoahHeuristics(ShenandoahSpaceInfo* space_info) : } ShenandoahHeuristics::~ShenandoahHeuristics() { - FREE_C_HEAP_ARRAY(RegionGarbage, _region_data); + FREE_C_HEAP_ARRAY(_region_data); } void ShenandoahHeuristics::choose_collection_set(ShenandoahCollectionSet* collection_set) { ShenandoahHeap* heap = ShenandoahHeap::heap(); assert(collection_set->is_empty(), "Must be empty"); - assert(!heap->mode()->is_generational(), "Wrong heuristic for heap mode"); - - // Check all pinned regions have updated status before choosing the collection set. - heap->assert_pinned_region_status(); // Step 1. Build up the region candidates we care about, rejecting losers and accepting winners right away. @@ -103,6 +100,10 @@ void ShenandoahHeuristics::choose_collection_set(ShenandoahCollectionSet* collec for (size_t i = 0; i < num_regions; i++) { ShenandoahHeapRegion* region = heap->get_region(i); + if (!_space_info->contains(region)) { + continue; + } + size_t garbage = region->garbage(); total_garbage += garbage; @@ -117,6 +118,8 @@ void ShenandoahHeuristics::choose_collection_set(ShenandoahCollectionSet* collec region->make_trash_immediate(); } else { // This is our candidate for later consideration. + assert(region->get_top_before_promote() == nullptr, + "Cannot add region %zu scheduled for in-place-promotion to the collection set", i); candidates[cand_idx].set_region_and_garbage(region, garbage); cand_idx++; } @@ -149,6 +152,7 @@ void ShenandoahHeuristics::choose_collection_set(ShenandoahCollectionSet* collec choose_collection_set_from_regiondata(collection_set, candidates, cand_idx, immediate_garbage + free); } collection_set->summarize(total_garbage, immediate_garbage, immediate_regions); + ShenandoahTracer::report_evacuation_info(collection_set, free_regions, immediate_regions, immediate_garbage); } void ShenandoahHeuristics::start_idle_span() { diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp index 4f71562f4f6..0789fd5cb1c 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp @@ -369,7 +369,6 @@ bool ShenandoahOldHeuristics::top_off_collection_set(ShenandoahCollectionSet* co assert(consumed_from_young_cset <= max_young_cset, "sanity"); assert(max_young_cset <= young_unaffiliated_regions * region_size_bytes, "sanity"); - size_t regions_for_old_expansion; if (consumed_from_young_cset < max_young_cset) { size_t excess_young_reserves = max_young_cset - consumed_from_young_cset; @@ -392,7 +391,10 @@ bool ShenandoahOldHeuristics::top_off_collection_set(ShenandoahCollectionSet* co _unspent_unfragmented_old_budget += supplement_without_waste; _old_generation->augment_evacuation_reserve(budget_supplement); young_generation->set_evacuation_reserve(max_young_cset - budget_supplement); - + assert(young_generation->get_evacuation_reserve() >= + collection_set->get_live_bytes_in_untenurable_regions() * ShenandoahEvacWaste, + "adjusted evac reserve (%zu) must be large enough for planned evacuation (%zu)", + young_generation->get_evacuation_reserve(), collection_set->get_live_bytes_in_untenurable_regions()); return add_old_regions_to_cset(collection_set); } else { add_regions_to_old = 0; @@ -574,7 +576,7 @@ void ShenandoahOldHeuristics::prepare_for_old_collections() { } else if (has_coalesce_and_fill_candidates()) { _old_generation->transition_to(ShenandoahOldGeneration::FILLING); } else { - _old_generation->transition_to(ShenandoahOldGeneration::WAITING_FOR_BOOTSTRAP); + _old_generation->transition_to(ShenandoahOldGeneration::IDLE); } } diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahSpaceInfo.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahSpaceInfo.hpp index 6ed05abf0b1..765061a43ed 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahSpaceInfo.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahSpaceInfo.hpp @@ -27,6 +27,8 @@ #include "utilities/globalDefinitions.hpp" +class ShenandoahHeapRegion; + /* * The purpose of this interface is to decouple the heuristics from a * direct dependency on the ShenandoahHeap singleton instance. This is @@ -46,6 +48,9 @@ public: // in time within each GC cycle. For certain GC cycles, the value returned may include some bytes allocated before // the start of the current GC cycle. virtual size_t bytes_allocated_since_gc_start() const = 0; + + // Return true if this region belongs to this space. + virtual bool contains(ShenandoahHeapRegion* region) const = 0; }; #endif //SHARE_GC_SHENANDOAH_HEURISTICS_SHENANDOAHSPACEINFO_HPP diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp index 68ffb6592db..27aa9a47510 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp @@ -37,9 +37,9 @@ ShenandoahYoungHeuristics::ShenandoahYoungHeuristics(ShenandoahYoungGeneration* } -void ShenandoahYoungHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, - RegionData* data, size_t size, - size_t actual_free) { +void ShenandoahYoungHeuristics::select_collection_set_regions(ShenandoahCollectionSet* cset, + RegionData* data, size_t size, + size_t actual_free) { // See comments in ShenandoahAdaptiveHeuristics::choose_collection_set_from_regiondata(): // we do the same here, but with the following adjustments for generational mode: // diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.hpp index 806cef673d5..8fabc40693c 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.hpp @@ -38,9 +38,9 @@ public: explicit ShenandoahYoungHeuristics(ShenandoahYoungGeneration* generation); - void choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, - RegionData* data, size_t size, - size_t actual_free) override; + void select_collection_set_regions(ShenandoahCollectionSet* cset, + RegionData* data, size_t size, + size_t actual_free) override; bool should_start_gc() override; diff --git a/src/hotspot/share/gc/shenandoah/mode/shenandoahMode.cpp b/src/hotspot/share/gc/shenandoah/mode/shenandoahMode.cpp index 5ef21719ed4..1c2c15c40dc 100644 --- a/src/hotspot/share/gc/shenandoah/mode/shenandoahMode.cpp +++ b/src/hotspot/share/gc/shenandoah/mode/shenandoahMode.cpp @@ -26,7 +26,6 @@ #include "gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp" #include "gc/shenandoah/heuristics/shenandoahAggressiveHeuristics.hpp" #include "gc/shenandoah/heuristics/shenandoahCompactHeuristics.hpp" -#include "gc/shenandoah/heuristics/shenandoahSpaceInfo.hpp" #include "gc/shenandoah/heuristics/shenandoahStaticHeuristics.hpp" #include "gc/shenandoah/mode/shenandoahMode.hpp" diff --git a/src/hotspot/share/gc/shenandoah/mode/shenandoahPassiveMode.cpp b/src/hotspot/share/gc/shenandoah/mode/shenandoahPassiveMode.cpp index 41b2703730b..cc098bc5a21 100644 --- a/src/hotspot/share/gc/shenandoah/mode/shenandoahPassiveMode.cpp +++ b/src/hotspot/share/gc/shenandoah/mode/shenandoahPassiveMode.cpp @@ -50,7 +50,6 @@ void ShenandoahPassiveMode::initialize_flags() const { SHENANDOAH_ERGO_DISABLE_FLAG(ShenandoahSATBBarrier); SHENANDOAH_ERGO_DISABLE_FLAG(ShenandoahCASBarrier); SHENANDOAH_ERGO_DISABLE_FLAG(ShenandoahCloneBarrier); - SHENANDOAH_ERGO_DISABLE_FLAG(ShenandoahStackWatermarkBarrier); SHENANDOAH_ERGO_DISABLE_FLAG(ShenandoahCardBarrier); } diff --git a/src/hotspot/share/gc/shenandoah/mode/shenandoahSATBMode.cpp b/src/hotspot/share/gc/shenandoah/mode/shenandoahSATBMode.cpp index 7ac2d7b818f..e27aa90542d 100644 --- a/src/hotspot/share/gc/shenandoah/mode/shenandoahSATBMode.cpp +++ b/src/hotspot/share/gc/shenandoah/mode/shenandoahSATBMode.cpp @@ -42,6 +42,5 @@ void ShenandoahSATBMode::initialize_flags() const { SHENANDOAH_CHECK_FLAG_SET(ShenandoahSATBBarrier); SHENANDOAH_CHECK_FLAG_SET(ShenandoahCASBarrier); SHENANDOAH_CHECK_FLAG_SET(ShenandoahCloneBarrier); - SHENANDOAH_CHECK_FLAG_SET(ShenandoahStackWatermarkBarrier); SHENANDOAH_CHECK_FLAG_UNSET(ShenandoahCardBarrier); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.cpp b/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.cpp index 86ff6f22c72..4989c929b32 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.cpp @@ -57,36 +57,28 @@ ShenandoahAgeCensus::ShenandoahAgeCensus(uint max_workers) // Sentinel value _tenuring_threshold[i] = MAX_COHORTS; } - if (ShenandoahGenerationalAdaptiveTenuring) { - _local_age_tables = NEW_C_HEAP_ARRAY(AgeTable*, _max_workers, mtGC); - CENSUS_NOISE(_local_noise = NEW_C_HEAP_ARRAY(ShenandoahNoiseStats, max_workers, mtGC);) - for (uint i = 0; i < _max_workers; i++) { - _local_age_tables[i] = new AgeTable(false); - CENSUS_NOISE(_local_noise[i].clear();) - } - } else { - _local_age_tables = nullptr; + _local_age_tables = NEW_C_HEAP_ARRAY(AgeTable*, _max_workers, mtGC); + CENSUS_NOISE(_local_noise = NEW_C_HEAP_ARRAY(ShenandoahNoiseStats, max_workers, mtGC);) + for (uint i = 0; i < _max_workers; i++) { + _local_age_tables[i] = new AgeTable(false); + CENSUS_NOISE(_local_noise[i].clear();) } _epoch = MAX_SNAPSHOTS - 1; // see prepare_for_census_update() - - if (!ShenandoahGenerationalAdaptiveTenuring) { - _tenuring_threshold[_epoch] = InitialTenuringThreshold; - } } ShenandoahAgeCensus::~ShenandoahAgeCensus() { for (uint i = 0; i < MAX_SNAPSHOTS; i++) { delete _global_age_tables[i]; } - FREE_C_HEAP_ARRAY(AgeTable*, _global_age_tables); - FREE_C_HEAP_ARRAY(uint, _tenuring_threshold); - CENSUS_NOISE(FREE_C_HEAP_ARRAY(ShenandoahNoiseStats, _global_noise)); + FREE_C_HEAP_ARRAY(_global_age_tables); + FREE_C_HEAP_ARRAY(_tenuring_threshold); + CENSUS_NOISE(FREE_C_HEAP_ARRAY(_global_noise)); if (_local_age_tables) { for (uint i = 0; i < _max_workers; i++) { delete _local_age_tables[i]; } - FREE_C_HEAP_ARRAY(AgeTable*, _local_age_tables); - CENSUS_NOISE(FREE_C_HEAP_ARRAY(ShenandoahNoiseStats, _local_noise)); + FREE_C_HEAP_ARRAY(_local_age_tables); + CENSUS_NOISE(FREE_C_HEAP_ARRAY(_local_noise)); } } @@ -154,7 +146,6 @@ void ShenandoahAgeCensus::prepare_for_census_update() { // and compute the new tenuring threshold. void ShenandoahAgeCensus::update_census(size_t age0_pop) { prepare_for_census_update(); - assert(ShenandoahGenerationalAdaptiveTenuring, "Only update census when adaptive tenuring is enabled"); assert(_global_age_tables[_epoch]->is_clear(), "Dirty decks"); CENSUS_NOISE(assert(_global_noise[_epoch].is_clear(), "Dirty decks");) @@ -180,6 +171,15 @@ void ShenandoahAgeCensus::update_census(size_t age0_pop) { NOT_PRODUCT(update_total();) } +size_t ShenandoahAgeCensus::get_tenurable_bytes(const uint tenuring_threshold) const { + assert(_epoch < MAX_SNAPSHOTS, "Out of bounds"); + size_t total = 0; + const AgeTable* pv = _global_age_tables[_epoch]; + for (uint i = tenuring_threshold; i < MAX_COHORTS; i++) { + total += pv->sizes[i]; + } + return total * HeapWordSize; +} // Reset the epoch for the global age tables, // clearing all history. @@ -195,10 +195,6 @@ void ShenandoahAgeCensus::reset_global() { // Reset the local age tables, clearing any partial census. void ShenandoahAgeCensus::reset_local() { - if (!ShenandoahGenerationalAdaptiveTenuring) { - assert(_local_age_tables == nullptr, "Error"); - return; - } for (uint i = 0; i < _max_workers; i++) { _local_age_tables[i]->clear(); CENSUS_NOISE(_local_noise[i].clear();) @@ -221,10 +217,6 @@ bool ShenandoahAgeCensus::is_clear_global() { // Is local census information clear? bool ShenandoahAgeCensus::is_clear_local() { - if (!ShenandoahGenerationalAdaptiveTenuring) { - assert(_local_age_tables == nullptr, "Error"); - return true; - } for (uint i = 0; i < _max_workers; i++) { bool clear = _local_age_tables[i]->is_clear(); CENSUS_NOISE(clear |= _local_noise[i].is_clear();) @@ -258,7 +250,6 @@ void ShenandoahAgeCensus::update_total() { #endif // !PRODUCT void ShenandoahAgeCensus::update_tenuring_threshold() { - assert(ShenandoahGenerationalAdaptiveTenuring, "Only update when adaptive tenuring is enabled"); uint tt = compute_tenuring_threshold(); assert(tt <= MAX_COHORTS, "Out of bounds"); _tenuring_threshold[_epoch] = tt; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.hpp b/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.hpp index 39ea4ee9002..c140f445e21 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.hpp @@ -97,6 +97,8 @@ struct ShenandoahNoiseStats { // once the per-worker data is consolidated into the appropriate population vector // per minor collection. The _local_age_table is thus C x N, for N GC workers. class ShenandoahAgeCensus: public CHeapObj { + friend class ShenandoahTenuringOverride; + AgeTable** _global_age_tables; // Global age tables used for adapting tenuring threshold, one per snapshot AgeTable** _local_age_tables; // Local scratch age tables to track object ages, one per worker @@ -148,6 +150,10 @@ class ShenandoahAgeCensus: public CHeapObj { return _tenuring_threshold[prev]; } + // Override the tenuring threshold for the current epoch. This is used to + // cause everything to be promoted for a whitebox full gc request. + void set_tenuring_threshold(uint threshold) { _tenuring_threshold[_epoch] = threshold; } + #ifndef PRODUCT // Return the sum of size of objects of all ages recorded in the // census at snapshot indexed by snap. @@ -173,7 +179,6 @@ class ShenandoahAgeCensus: public CHeapObj { ~ShenandoahAgeCensus(); // Return the local age table (population vector) for worker_id. - // Only used in the case of ShenandoahGenerationalAdaptiveTenuring AgeTable* get_local_age_table(uint worker_id) const { return _local_age_tables[worker_id]; } @@ -211,6 +216,12 @@ class ShenandoahAgeCensus: public CHeapObj { // allocated when the concurrent marking was in progress. void update_census(size_t age0_pop); + // Return the total size of the population at or above the given threshold for the current epoch + size_t get_tenurable_bytes(uint tenuring_threshold) const; + + // As above, but use the current tenuring threshold + size_t get_tenurable_bytes() const { return get_tenurable_bytes(tenuring_threshold()); } + // Reset the epoch, clearing accumulated census history // Note: this isn't currently used, but reserved for planned // future usage. @@ -233,4 +244,26 @@ class ShenandoahAgeCensus: public CHeapObj { void print(); }; +// RAII object that temporarily overrides the tenuring threshold for the +// duration of a scope, restoring the original value on destruction. +// Used to force promotion of all young objects during whitebox full GCs. +class ShenandoahTenuringOverride : public StackObj { + ShenandoahAgeCensus* _census; + uint _saved_threshold; + bool _active; +public: + ShenandoahTenuringOverride(bool active, ShenandoahAgeCensus* census) : + _census(census), _saved_threshold(0), _active(active) { + if (_active) { + _saved_threshold = _census->tenuring_threshold(); + _census->set_tenuring_threshold(0); + } + } + ~ShenandoahTenuringOverride() { + if (_active) { + _census->set_tenuring_threshold(_saved_threshold); + } + } +}; + #endif // SHARE_GC_SHENANDOAH_SHENANDOAHAGECENSUS_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp b/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp index c1fa4b964b7..e9d6a686694 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp @@ -200,6 +200,7 @@ void ShenandoahArguments::initialize() { && strcmp(ShenandoahGCHeuristics, "adaptive") != 0) { log_warning(gc)("Ignoring -XX:ShenandoahGCHeuristics input: %s, because generational shenandoah only" " supports adaptive heuristics", ShenandoahGCHeuristics); + FLAG_SET_ERGO(ShenandoahGCHeuristics, "adaptive"); } FullGCForwarding::initialize_flags(MaxHeapSize); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp b/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp index baeaffb9c7b..268f5b13035 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp @@ -559,26 +559,24 @@ bool ShenandoahAsserts::extract_klass_safely(oop obj, narrowKlass& nk, const Kla if (!os::is_readable_pointer(obj)) { return false; } - if (UseCompressedClassPointers) { - if (UseCompactObjectHeaders) { // look in forwardee - markWord mark = obj->mark(); - if (mark.is_marked()) { - oop fwd = cast_to_oop(mark.clear_lock_bits().to_pointer()); - if (!os::is_readable_pointer(fwd)) { - return false; - } - mark = fwd->mark(); + + if (UseCompactObjectHeaders) { // look in forwardee + markWord mark = obj->mark(); + if (mark.is_marked()) { + oop fwd = cast_to_oop(mark.clear_lock_bits().to_pointer()); + if (!os::is_readable_pointer(fwd)) { + return false; } - nk = mark.narrow_klass(); - } else { - nk = obj->narrow_klass(); + mark = fwd->mark(); } - if (!CompressedKlassPointers::is_valid_narrow_klass_id(nk)) { - return false; - } - k = CompressedKlassPointers::decode_not_null_without_asserts(nk); + nk = mark.narrow_klass(); } else { - k = obj->klass(); + nk = obj->narrow_klass(); } + if (!CompressedKlassPointers::is_valid_narrow_klass_id(nk)) { + return false; + } + k = CompressedKlassPointers::decode_not_null_without_asserts(nk); + return k != nullptr; } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp index cb6ff795c07..0949959b042 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp @@ -149,11 +149,9 @@ void ShenandoahBarrierSet::on_thread_attach(Thread *thread) { BarrierSetNMethod* bs_nm = barrier_set_nmethod(); thread->set_nmethod_disarmed_guard_value(bs_nm->disarmed_guard_value()); - if (ShenandoahStackWatermarkBarrier) { - JavaThread* const jt = JavaThread::cast(thread); - StackWatermark* const watermark = new ShenandoahStackWatermark(jt); - StackWatermarkSet::add_watermark(jt, watermark); - } + JavaThread* const jt = JavaThread::cast(thread); + StackWatermark* const watermark = new ShenandoahStackWatermark(jt); + StackWatermarkSet::add_watermark(jt, watermark); } } @@ -172,14 +170,12 @@ void ShenandoahBarrierSet::on_thread_detach(Thread *thread) { } // SATB protocol requires to keep alive reachable oops from roots at the beginning of GC - if (ShenandoahStackWatermarkBarrier) { - if (_heap->is_concurrent_mark_in_progress()) { - ShenandoahKeepAliveClosure oops; - StackWatermarkSet::finish_processing(JavaThread::cast(thread), &oops, StackWatermarkKind::gc); - } else if (_heap->is_concurrent_weak_root_in_progress() && _heap->is_evacuation_in_progress()) { - ShenandoahContextEvacuateUpdateRootsClosure oops; - StackWatermarkSet::finish_processing(JavaThread::cast(thread), &oops, StackWatermarkKind::gc); - } + if (_heap->is_concurrent_mark_in_progress()) { + ShenandoahKeepAliveClosure oops; + StackWatermarkSet::finish_processing(JavaThread::cast(thread), &oops, StackWatermarkKind::gc); + } else if (_heap->is_concurrent_weak_root_in_progress() && _heap->is_evacuation_in_progress()) { + ShenandoahContextEvacuateUpdateRootsClosure oops; + StackWatermarkSet::finish_processing(JavaThread::cast(thread), &oops, StackWatermarkKind::gc); } } } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahClosures.hpp b/src/hotspot/share/gc/shenandoah/shenandoahClosures.hpp index fefed0340c4..9ab45380c61 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahClosures.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahClosures.hpp @@ -180,9 +180,6 @@ public: }; class ShenandoahNMethodAndDisarmClosure : public NMethodToOopClosure { -private: - BarrierSetNMethod* const _bs; - public: inline ShenandoahNMethodAndDisarmClosure(OopClosure* cl); inline void do_nmethod(nmethod* nm); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp index e8d25b1e5a9..96ecbad1145 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp @@ -209,15 +209,13 @@ void ShenandoahCleanUpdateWeakOopsClosure::do_oo } ShenandoahNMethodAndDisarmClosure::ShenandoahNMethodAndDisarmClosure(OopClosure* cl) : - NMethodToOopClosure(cl, true /* fix_relocations */), - _bs(BarrierSet::barrier_set()->barrier_set_nmethod()) { -} + NMethodToOopClosure(cl, true /* fix_relocations */) {} void ShenandoahNMethodAndDisarmClosure::do_nmethod(nmethod* nm) { assert(nm != nullptr, "Sanity"); assert(!ShenandoahNMethod::gc_data(nm)->is_unregistered(), "Should not be here"); NMethodToOopClosure::do_nmethod(nm); - _bs->disarm(nm); + ShenandoahNMethod::disarm_nmethod(nm); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp b/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp index 64e135e9a4e..7cf60cdf65c 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp @@ -40,20 +40,6 @@ ShenandoahNMethodTable* ShenandoahCodeRoots::_nmethod_table; int ShenandoahCodeRoots::_disarmed_value = 1; -bool ShenandoahCodeRoots::use_nmethod_barriers_for_mark() { - // Continuations need nmethod barriers for scanning stack chunk nmethods. - if (Continuations::enabled()) return true; - - // Concurrent class unloading needs nmethod barriers. - // When a nmethod is about to be executed, we need to make sure that all its - // metadata are marked. The alternative is to remark thread roots at final mark - // pause, which would cause latency issues. - if (ShenandoahHeap::heap()->unload_classes()) return true; - - // Otherwise, we can go without nmethod barriers. - return false; -} - void ShenandoahCodeRoots::initialize() { _nmethod_table = new ShenandoahNMethodTable(); } @@ -68,27 +54,14 @@ void ShenandoahCodeRoots::unregister_nmethod(nmethod* nm) { _nmethod_table->unregister_nmethod(nm); } -void ShenandoahCodeRoots::arm_nmethods_for_mark() { - if (use_nmethod_barriers_for_mark()) { - BarrierSet::barrier_set()->barrier_set_nmethod()->arm_all_nmethods(); - } -} - -void ShenandoahCodeRoots::arm_nmethods_for_evac() { +void ShenandoahCodeRoots::arm_nmethods() { BarrierSet::barrier_set()->barrier_set_nmethod()->arm_all_nmethods(); } class ShenandoahDisarmNMethodClosure : public NMethodClosure { -private: - BarrierSetNMethod* const _bs; - public: - ShenandoahDisarmNMethodClosure() : - _bs(BarrierSet::barrier_set()->barrier_set_nmethod()) { - } - virtual void do_nmethod(nmethod* nm) { - _bs->disarm(nm); + ShenandoahNMethod::disarm_nmethod(nm); } }; @@ -111,10 +84,8 @@ public: }; void ShenandoahCodeRoots::disarm_nmethods() { - if (use_nmethod_barriers_for_mark()) { - ShenandoahDisarmNMethodsTask task; - ShenandoahHeap::heap()->workers()->run_task(&task); - } + ShenandoahDisarmNMethodsTask task; + ShenandoahHeap::heap()->workers()->run_task(&task); } class ShenandoahNMethodUnlinkClosure : public NMethodClosure { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.hpp b/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.hpp index d29c446f210..d395b4516f4 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.hpp @@ -67,14 +67,11 @@ public: // Concurrent nmethod unloading support static void unlink(WorkerThreads* workers, bool unloading_occurred); static void purge(); - static void arm_nmethods_for_mark(); - static void arm_nmethods_for_evac(); + static void arm_nmethods(); static void disarm_nmethods(); static int disarmed_value() { return _disarmed_value; } static int* disarmed_value_address() { return &_disarmed_value; } - static bool use_nmethod_barriers_for_mark(); - private: static ShenandoahNMethodTable* _nmethod_table; static int _disarmed_value; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCollectorPolicy.cpp b/src/hotspot/share/gc/shenandoah/shenandoahCollectorPolicy.cpp index bbd9dca1513..cfa79fc055e 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahCollectorPolicy.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahCollectorPolicy.cpp @@ -257,10 +257,10 @@ void ShenandoahCollectorPolicy::print_gc_stats(outputStream* out) const { out->print_cr("%5zu Full GCs (%.2f%%)", _success_full_gcs, percent_of(_success_full_gcs, completed_gcs)); if (!ExplicitGCInvokesConcurrent) { - out->print_cr(" %5zu invoked explicitly (%.2f%%)", explicit_requests, percent_of(explicit_requests, _success_concurrent_gcs)); + out->print_cr(" %5zu invoked explicitly (%.2f%%)", explicit_requests, percent_of(explicit_requests, _success_full_gcs)); } if (!ShenandoahImplicitGCInvokesConcurrent) { - out->print_cr(" %5zu invoked implicitly (%.2f%%)", implicit_requests, percent_of(implicit_requests, _success_concurrent_gcs)); + out->print_cr(" %5zu invoked implicitly (%.2f%%)", implicit_requests, percent_of(implicit_requests, _success_full_gcs)); } out->print_cr(" %5zu caused by allocation failure (%.2f%%)", _alloc_failure_full, percent_of(_alloc_failure_full, _success_full_gcs)); out->print_cr(" %5zu upgraded from Degenerated GC (%.2f%%)", _alloc_failure_degenerated_upgrade_to_full, percent_of(_alloc_failure_degenerated_upgrade_to_full, _success_full_gcs)); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp index f0125c38cae..6723bb89021 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp @@ -112,6 +112,24 @@ void ShenandoahConcurrentGC::entry_concurrent_update_refs_prepare(ShenandoahHeap heap->concurrent_prepare_for_update_refs(); } +void ShenandoahConcurrentGC::entry_update_card_table() { + ShenandoahHeap* const heap = ShenandoahHeap::heap(); + TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); + + static const char* msg = "Concurrent update cards"; + ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_update_card_table); + EventMark em("%s", msg); + + ShenandoahWorkerScope scope(heap->workers(), + ShenandoahWorkerPolicy::calc_workers_for_conc_evac(), + "concurrent update cards"); + + // Heap needs to be parsable here. + // Also, parallel heap region iterate must have a phase set. + assert(ShenandoahTimingsTracker::is_current_phase_valid(), "Current phase must be set"); + ShenandoahGenerationalHeap::heap()->old_generation()->update_card_table(); +} + bool ShenandoahConcurrentGC::collect(GCCause::Cause cause) { ShenandoahHeap* const heap = ShenandoahHeap::heap(); _generation->ref_processor()->set_soft_reference_policy( @@ -206,6 +224,11 @@ bool ShenandoahConcurrentGC::collect(GCCause::Cause cause) { // Perform update-refs phase. entry_concurrent_update_refs_prepare(heap); + + if (ShenandoahHeap::heap()->mode()->is_generational()) { + entry_update_card_table(); + } + if (ShenandoahVerify) { vmop_entry_init_update_refs(); } @@ -232,8 +255,10 @@ bool ShenandoahConcurrentGC::collect(GCCause::Cause cause) { return false; } - if (VerifyAfterGC) { - vmop_entry_verify_final_roots(); + // In normal cycle, final-update-refs would verify at the end of the cycle. + // In abbreviated cycle, we need to verify separately. + if (ShenandoahVerify) { + vmop_entry_final_verify(); } } @@ -321,14 +346,14 @@ void ShenandoahConcurrentGC::vmop_entry_final_update_refs() { VMThread::execute(&op); } -void ShenandoahConcurrentGC::vmop_entry_verify_final_roots() { +void ShenandoahConcurrentGC::vmop_entry_final_verify() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); TraceCollectorStats tcs(heap->monitoring_support()->stw_collection_counters()); - ShenandoahTimingsTracker timing(ShenandoahPhaseTimings::final_roots_gross); + ShenandoahTimingsTracker timing(ShenandoahPhaseTimings::final_verify_gross); // This phase does not use workers, no need for setup heap->try_inject_alloc_failure(); - VM_ShenandoahFinalRoots op(this); + VM_ShenandoahFinalVerify op(this); VMThread::execute(&op); } @@ -377,12 +402,12 @@ void ShenandoahConcurrentGC::entry_final_update_refs() { op_final_update_refs(); } -void ShenandoahConcurrentGC::entry_verify_final_roots() { - const char* msg = verify_final_roots_event_message(); - ShenandoahPausePhase gc_phase(msg, ShenandoahPhaseTimings::final_roots); +void ShenandoahConcurrentGC::entry_final_verify() { + const char* msg = verify_final_event_message(); + ShenandoahPausePhase gc_phase(msg, ShenandoahPhaseTimings::final_verify); EventMark em("%s", msg); - op_verify_final_roots(); + op_verify_final(); } void ShenandoahConcurrentGC::entry_reset() { @@ -721,9 +746,8 @@ void ShenandoahConcurrentGC::op_init_mark() { // Make above changes visible to worker threads OrderAccess::fence(); - // Arm nmethods for concurrent mark - ShenandoahCodeRoots::arm_nmethods_for_mark(); - + // Arm nmethods/stack for concurrent processing + ShenandoahCodeRoots::arm_nmethods(); ShenandoahStackWatermark::change_epoch_id(); { @@ -782,7 +806,7 @@ void ShenandoahConcurrentGC::op_final_mark() { heap->set_has_forwarded_objects(true); // Arm nmethods/stack for concurrent processing - ShenandoahCodeRoots::arm_nmethods_for_evac(); + ShenandoahCodeRoots::arm_nmethods(); ShenandoahStackWatermark::change_epoch_id(); } else { @@ -1012,14 +1036,10 @@ void ShenandoahConcurrentGC::op_class_unloading() { class ShenandoahEvacUpdateCodeCacheClosure : public NMethodClosure { private: - BarrierSetNMethod* const _bs; ShenandoahEvacuateUpdateMetadataClosure _cl; public: - ShenandoahEvacUpdateCodeCacheClosure() : - _bs(BarrierSet::barrier_set()->barrier_set_nmethod()), - _cl() { - } + ShenandoahEvacUpdateCodeCacheClosure() : _cl() {} void do_nmethod(nmethod* n) { ShenandoahNMethod* data = ShenandoahNMethod::gc_data(n); @@ -1027,8 +1047,8 @@ public: // Setup EvacOOM scope below reentrant lock to avoid deadlock with // nmethod_entry_barrier ShenandoahEvacOOMScope oom; - data->oops_do(&_cl, true/*fix relocation*/); - _bs->disarm(n); + data->oops_do(&_cl, /* fix_relocations = */ true); + ShenandoahNMethod::disarm_nmethod(n); } }; @@ -1245,10 +1265,10 @@ bool ShenandoahConcurrentGC::entry_final_roots() { return true; } -void ShenandoahConcurrentGC::op_verify_final_roots() { - if (VerifyAfterGC) { - Universe::verify(); - } +void ShenandoahConcurrentGC::op_verify_final() { + assert(ShenandoahVerify, "Should have been checked before"); + ShenandoahHeap* const heap = ShenandoahHeap::heap(); + heap->verifier()->verify_after_gc(_generation); } void ShenandoahConcurrentGC::op_cleanup_complete() { @@ -1333,11 +1353,11 @@ const char* ShenandoahConcurrentGC::conc_reset_after_collect_event_message() con } } -const char* ShenandoahConcurrentGC::verify_final_roots_event_message() const { +const char* ShenandoahConcurrentGC::verify_final_event_message() const { if (ShenandoahHeap::heap()->unload_classes()) { - SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Pause Verify Final Roots", " (unload classes)"); + SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Pause Verify Final", " (unload classes)"); } else { - SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Pause Verify Final Roots", ""); + SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Pause Verify Final", ""); } } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.hpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.hpp index 54d43416fdb..fde585b4aa9 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.hpp @@ -43,7 +43,7 @@ class ShenandoahConcurrentGC : public ShenandoahGC { friend class VM_ShenandoahFinalMarkStartEvac; friend class VM_ShenandoahInitUpdateRefs; friend class VM_ShenandoahFinalUpdateRefs; - friend class VM_ShenandoahFinalRoots; + friend class VM_ShenandoahFinalVerify; protected: ShenandoahConcurrentMark _mark; @@ -59,8 +59,6 @@ public: bool collect(GCCause::Cause cause) override; ShenandoahDegenPoint degen_point() const; - void entry_concurrent_update_refs_prepare(ShenandoahHeap* heap); - // Return true if this cycle found enough immediate garbage to skip evacuation bool abbreviated() const { return _abbreviated; } @@ -71,7 +69,7 @@ protected: void vmop_entry_final_mark(); void vmop_entry_init_update_refs(); void vmop_entry_final_update_refs(); - void vmop_entry_verify_final_roots(); + void vmop_entry_final_verify(); // Entry methods to normally STW GC operations. These set up logging, monitoring // and workers for next VM operation @@ -79,7 +77,7 @@ protected: void entry_final_mark(); void entry_init_update_refs(); void entry_final_update_refs(); - void entry_verify_final_roots(); + void entry_final_verify(); // Entry methods to normally concurrent GC operations. These set up logging, monitoring // for concurrent operation. @@ -95,6 +93,8 @@ protected: void entry_cleanup_early(); void entry_evacuate(); void entry_update_thread_roots(); + void entry_update_card_table(); + void entry_concurrent_update_refs_prepare(ShenandoahHeap* heap); void entry_update_refs(); void entry_cleanup_complete(); @@ -122,7 +122,7 @@ protected: void op_update_thread_roots(); void op_final_update_refs(); - void op_verify_final_roots(); + void op_verify_final(); void op_cleanup_complete(); void op_reset_after_collect(); @@ -143,7 +143,7 @@ private: // passing around the logging/tracing systems const char* init_mark_event_message() const; const char* final_mark_event_message() const; - const char* verify_final_roots_event_message() const; + const char* verify_final_event_message() const; const char* conc_final_roots_event_message() const; const char* conc_mark_event_message() const; const char* conc_reset_event_message() const; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp index 48183507124..6175f15676c 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp @@ -138,7 +138,10 @@ void ShenandoahControlThread::run_service() { heuristics->cancel_trigger_request(); - heap->reset_bytes_allocated_since_gc_start(); + if (mode != stw_degenerated) { + // If mode is stw_degenerated, count bytes allocated from the start of the conc GC that experienced alloc failure. + heap->reset_bytes_allocated_since_gc_start(); + } MetaspaceCombinedStats meta_sizes = MetaspaceUtils::get_combined_statistics(); @@ -343,7 +346,8 @@ void ShenandoahControlThread::service_stw_full_cycle(GCCause::Cause cause) { void ShenandoahControlThread::service_stw_degenerated_cycle(GCCause::Cause cause, ShenandoahGC::ShenandoahDegenPoint point) { assert (point != ShenandoahGC::_degenerated_unset, "Degenerated point should be set"); ShenandoahHeap* const heap = ShenandoahHeap::heap(); - ShenandoahGCSession session(cause, heap->global_generation()); + ShenandoahGCSession session(cause, heap->global_generation(), true, + point == ShenandoahGC::ShenandoahDegenPoint::_degenerated_outside_cycle); heap->increment_total_collections(false); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahController.cpp b/src/hotspot/share/gc/shenandoah/shenandoahController.cpp index 50aabad7d42..0096aad2570 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahController.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahController.cpp @@ -23,13 +23,13 @@ * */ +#include "gc/shared/allocTracer.hpp" #include "gc/shared/gc_globals.hpp" #include "gc/shenandoah/shenandoahCollectorPolicy.hpp" #include "gc/shenandoah/shenandoahController.hpp" #include "gc/shenandoah/shenandoahHeap.hpp" #include "gc/shenandoah/shenandoahHeapRegion.inline.hpp" - void ShenandoahController::update_gc_id() { _gc_id.add_then_fetch((size_t)1); } @@ -45,10 +45,12 @@ void ShenandoahController::handle_alloc_failure(const ShenandoahAllocRequest& re const GCCause::Cause cause = is_humongous ? GCCause::_shenandoah_humongous_allocation_failure : GCCause::_allocation_failure; ShenandoahHeap* const heap = ShenandoahHeap::heap(); + size_t req_byte = req.size() * HeapWordSize; if (heap->cancel_gc(cause)) { - log_info(gc)("Failed to allocate %s, " PROPERFMT, req.type_string(), PROPERFMTARGS(req.size() * HeapWordSize)); + log_info(gc)("Failed to allocate %s, " PROPERFMT, req.type_string(), PROPERFMTARGS(req_byte)); request_gc(cause); } + AllocTracer::send_allocation_requiring_gc_event(req_byte, checked_cast(get_gc_id())); if (block) { MonitorLocker ml(&_alloc_failure_waiters_lock); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp index 8cd8a390c4a..84b22f13d47 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp @@ -55,7 +55,7 @@ bool ShenandoahDegenGC::collect(GCCause::Cause cause) { vmop_degenerated(); ShenandoahHeap* heap = ShenandoahHeap::heap(); if (heap->mode()->is_generational()) { - bool is_bootstrap_gc = heap->old_generation()->is_bootstrapping(); + bool is_bootstrap_gc = heap->young_generation()->is_bootstrap_cycle(); heap->mmu_tracker()->record_degenerated(GCId::current(), is_bootstrap_gc); const char* msg = is_bootstrap_gc? "At end of Degenerated Bootstrap Old GC": "At end of Degenerated Young GC"; heap->log_heap_status(msg); @@ -277,6 +277,11 @@ void ShenandoahDegenGC::op_degenerated() { _abbreviated = true; } + // labs are retired, walk the old regions and update remembered set + if (ShenandoahHeap::heap()->mode()->is_generational()) { + ShenandoahGenerationalHeap::heap()->old_generation()->update_card_table(); + } + case _degenerated_update_refs: if (heap->has_forwarded_objects()) { op_update_refs(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp index a579d6d3694..1807383123b 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp @@ -1569,7 +1569,7 @@ HeapWord* ShenandoahFreeSet::try_allocate_in(ShenandoahHeapRegion* r, Shenandoah // We must call try_recycle_under_lock() even if !r->is_trash(). The reason is that if r is being recycled at this // moment by a GC worker thread, it may appear to be not trash even though it has not yet been fully recycled. If // we proceed without waiting for the worker to finish recycling the region, the worker thread may overwrite the - // region's affiliation with FREE after we set the region's affiliation to req.afiliation() below + // region's affiliation with FREE after we set the region's affiliation to req.affiliation() below r->try_recycle_under_lock(); in_new_region = r->is_empty(); if (in_new_region) { @@ -1585,7 +1585,6 @@ HeapWord* ShenandoahFreeSet::try_allocate_in(ShenandoahHeapRegion* r, Shenandoah // concurrent preparations for mixed evacuations are completed), we mark this region as not requiring any // coalesce-and-fill processing. r->end_preemptible_coalesce_and_fill(); - _heap->old_generation()->clear_cards_for(r); } #ifdef ASSERT ShenandoahMarkingContext* const ctx = _heap->marking_context(); @@ -2679,8 +2678,11 @@ void ShenandoahFreeSet::reduce_young_reserve(size_t adjusted_young_reserve, size * 1. Memory currently available within old and young * 2. Trashed regions currently residing in young and old, which will become available momentarily * 3. The value of old_generation->get_region_balance() which represents the number of regions that we plan - * to transfer from old generation to young generation. Prior to each invocation of compute_young_and_old_reserves(), - * this value should computed by ShenandoahGenerationalHeap::compute_old_generation_balance(). + * to transfer from old generation to young generation. At the end of each GC cycle, we reset region_balance + * to zero. As we prepare to rebuild free set at the end of update-refs, we call + * ShenandoahGenerationalHeap::compute_old_generation_balance() to compute a new value of region_balance. + * This allows us to expand or shrink the size of the Old Collector reserves based on anticipated needs of + * the next GC cycle. */ void ShenandoahFreeSet::compute_young_and_old_reserves(size_t young_trashed_regions, size_t old_trashed_regions, size_t& young_reserve_result, size_t& old_reserve_result) const { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp index 2df06432bd2..eeff0fde87c 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp @@ -857,14 +857,16 @@ public: ShenandoahRebuildLocker locker(rebuild_lock()); return _partitions.available_in_locked_for_rebuild(ShenandoahFreeSetPartitionId::Mutator); } - inline size_t available_holding_lock() const - { return _partitions.available_in(ShenandoahFreeSetPartitionId::Mutator); } // Use this version of available() if the heap lock is held. inline size_t available_locked() const { return _partitions.available_in(ShenandoahFreeSetPartitionId::Mutator); } + inline size_t collector_available_locked() const { + return _partitions.available_in(ShenandoahFreeSetPartitionId::Collector); + } + inline size_t total_humongous_waste() const { return _total_humongous_waste; } inline size_t humongous_waste_in_mutator() const { return _partitions.humongous_waste(ShenandoahFreeSetPartitionId::Mutator); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp index 750f7e9122d..34092a744d5 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp @@ -261,7 +261,7 @@ void ShenandoahFullGC::do_it(GCCause::Cause gc_cause) { for (uint i = 0; i < heap->max_workers(); i++) { delete worker_slices[i]; } - FREE_C_HEAP_ARRAY(ShenandoahHeapRegionSet*, worker_slices); + FREE_C_HEAP_ARRAY(worker_slices); heap->set_full_gc_move_in_progress(false); heap->set_full_gc_in_progress(false); @@ -688,7 +688,7 @@ void ShenandoahFullGC::distribute_slices(ShenandoahHeapRegionSet** worker_slices } } - FREE_C_HEAP_ARRAY(size_t, live); + FREE_C_HEAP_ARRAY(live); #ifdef ASSERT ResourceBitMap map(n_regions); @@ -878,8 +878,11 @@ public: Copy::aligned_conjoint_words(compact_from, compact_to, size); oop new_obj = cast_to_oop(compact_to); - ContinuationGCSupport::relativize_stack_chunk(new_obj); + // Restore the mark word before relativizing the stack chunk. The copy's + // mark word contains the full GC forwarding encoding, which would cause + // is_stackChunk() to read garbage (especially with compact headers). new_obj->init_mark(); + ContinuationGCSupport::relativize_stack_chunk(new_obj); } } }; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp index 750389ae6c3..5b26ee67653 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp @@ -272,7 +272,7 @@ void ShenandoahGeneration::prepare_regions_and_collection_set(bool concurrent) { } // Tally the census counts and compute the adaptive tenuring threshold - if (is_generational && ShenandoahGenerationalAdaptiveTenuring) { + if (is_generational) { // Objects above TAMS weren't included in the age census. Since they were all // allocated in this cycle they belong in the age 0 cohort. We walk over all // young regions and sum the volume of objects between TAMS and top. @@ -301,6 +301,7 @@ void ShenandoahGeneration::prepare_regions_and_collection_set(bool concurrent) { collection_set->clear(); ShenandoahHeapLocker locker(heap->lock()); + heap->assert_pinned_region_status(this); _heuristics->choose_collection_set(collection_set); } @@ -309,16 +310,9 @@ void ShenandoahGeneration::prepare_regions_and_collection_set(bool concurrent) { ShenandoahGCPhase phase(concurrent ? ShenandoahPhaseTimings::final_rebuild_freeset : ShenandoahPhaseTimings::degen_gc_final_rebuild_freeset); ShenandoahHeapLocker locker(heap->lock()); - - // We are preparing for evacuation. + // At start of evacation, we do NOT compute_old_generation_balance() size_t young_trashed_regions, old_trashed_regions, first_old, last_old, num_old; _free_set->prepare_to_rebuild(young_trashed_regions, old_trashed_regions, first_old, last_old, num_old); - if (heap->mode()->is_generational()) { - ShenandoahGenerationalHeap* gen_heap = ShenandoahGenerationalHeap::heap(); - size_t allocation_runway = - gen_heap->young_generation()->heuristics()->bytes_of_allocation_runway_before_gc_trigger(young_trashed_regions); - gen_heap->compute_old_generation_balance(allocation_runway, old_trashed_regions, young_trashed_regions); - } _free_set->finish_rebuild(young_trashed_regions, old_trashed_regions, num_old); } } @@ -423,12 +417,6 @@ size_t ShenandoahGeneration::available() const { return result; } -// For ShenandoahYoungGeneration, Include the young available that may have been reserved for the Collector. -size_t ShenandoahGeneration::available_with_reserve() const { - size_t result = available(max_capacity()); - return result; -} - size_t ShenandoahGeneration::soft_mutator_available() const { size_t result = available(ShenandoahHeap::heap()->soft_max_capacity() * (100.0 - ShenandoahEvacReserve) / 100); return result; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp index 1a549be8988..9f8944127c0 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp @@ -63,7 +63,7 @@ private: // Return available assuming that we can allocate no more than capacity bytes within this generation. size_t available(size_t capacity) const; - public: +public: ShenandoahGeneration(ShenandoahGenerationType type, uint max_workers); ~ShenandoahGeneration(); @@ -96,7 +96,6 @@ private: virtual size_t max_capacity() const override = 0; size_t available() const override; - size_t available_with_reserve() const; // Returns the memory available based on the _soft_ max heap capacity (soft_max_heap - used). // The soft max heap size may be adjusted lower than the max heap size to cause the trigger @@ -144,7 +143,7 @@ private: virtual bool contains(ShenandoahAffiliation affiliation) const = 0; // Return true if this region is affiliated with this generation. - virtual bool contains(ShenandoahHeapRegion* region) const = 0; + virtual bool contains(ShenandoahHeapRegion* region) const override = 0; // Return true if this object is affiliated with this generation. virtual bool contains(oop obj) const = 0; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp index 1edff443ded..bbad82de1dc 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp @@ -24,6 +24,7 @@ * */ +#include "gc/shenandoah/shenandoahAgeCensus.hpp" #include "gc/shenandoah/shenandoahAsserts.hpp" #include "gc/shenandoah/shenandoahCollectorPolicy.hpp" #include "gc/shenandoah/shenandoahConcurrentGC.hpp" @@ -120,10 +121,11 @@ void ShenandoahGenerationalControlThread::check_for_request(ShenandoahGCRequest& assert(request.generation != nullptr, "Must know which generation to use for degenerated cycle"); } } else { - if (request.cause == GCCause::_shenandoah_concurrent_gc) { - // This is a regulator request. It is also possible that the regulator "canceled" an old mark, - // so we can clear that here. This clear operation will only clear the cancellation if it is - // a regulator request. + if (request.cause == GCCause::_shenandoah_concurrent_gc || ShenandoahCollectorPolicy::is_explicit_gc(request.cause)) { + // This is a regulator request or an explicit gc request. Note that an explicit gc request is allowed to + // "upgrade" a regulator request. It is possible that the regulator "canceled" an old mark, so we must + // clear that cancellation here or the explicit gc cycle will erroneously detect it as a cancellation. + // This clear operation will only clear the cancellation if it was set by regulator request. _heap->clear_cancellation(GCCause::_shenandoah_concurrent_gc); } request.generation = _requested_generation; @@ -254,7 +256,8 @@ void ShenandoahGenerationalControlThread::run_gc_cycle(const ShenandoahGCRequest GCIdMark gc_id_mark; - if (gc_mode() != servicing_old) { + if ((gc_mode() != servicing_old) && (gc_mode() != stw_degenerated)) { + // If mode is stw_degenerated, count bytes allocated from the start of the conc GC that experienced alloc failure. _heap->reset_bytes_allocated_since_gc_start(); } @@ -271,6 +274,12 @@ void ShenandoahGenerationalControlThread::run_gc_cycle(const ShenandoahGCRequest // Cannot uncommit bitmap slices during concurrent reset ShenandoahNoUncommitMark forbid_region_uncommit(_heap); + // When a whitebox full GC is requested, set the tenuring threshold to zero + // so that all young objects are promoted to old. This ensures that tests + // using WB.fullGC() to promote objects to old gen will not loop forever. + ShenandoahTenuringOverride tenuring_override(request.cause == GCCause::_wb_full_gc, + _heap->age_census()); + _heap->print_before_gc(); switch (gc_mode()) { case concurrent_normal: { @@ -413,12 +422,11 @@ void ShenandoahGenerationalControlThread::service_concurrent_old_cycle(const She } // Coalescing threads completed and nothing was cancelled. it is safe to transition from this state. - old_generation->transition_to(ShenandoahOldGeneration::WAITING_FOR_BOOTSTRAP); + old_generation->transition_to(ShenandoahOldGeneration::IDLE); return; } - case ShenandoahOldGeneration::WAITING_FOR_BOOTSTRAP: - old_generation->transition_to(ShenandoahOldGeneration::BOOTSTRAPPING); - case ShenandoahOldGeneration::BOOTSTRAPPING: { + case ShenandoahOldGeneration::IDLE: + old_generation->transition_to(ShenandoahOldGeneration::MARKING); // Configure the young generation's concurrent mark to put objects in // old regions into the concurrent mark queues associated with the old // generation. The young cycle will run as normal except that rather than @@ -441,8 +449,6 @@ void ShenandoahGenerationalControlThread::service_concurrent_old_cycle(const She // and init mark for the concurrent mark. All of that work will have been // done by the bootstrapping young cycle. set_gc_mode(servicing_old); - old_generation->transition_to(ShenandoahOldGeneration::MARKING); - } case ShenandoahOldGeneration::MARKING: { ShenandoahGCSession session(request.cause, old_generation); bool marking_complete = resume_concurrent_old_cycle(old_generation, request.cause); @@ -622,11 +628,10 @@ void ShenandoahGenerationalControlThread::service_stw_full_cycle(GCCause::Cause void ShenandoahGenerationalControlThread::service_stw_degenerated_cycle(const ShenandoahGCRequest& request) { assert(_degen_point != ShenandoahGC::_degenerated_unset, "Degenerated point should be set"); - request.generation->heuristics()->record_degenerated_cycle_start(ShenandoahGC::ShenandoahDegenPoint::_degenerated_outside_cycle - == _degen_point); _heap->increment_total_collections(false); - ShenandoahGCSession session(request.cause, request.generation); + ShenandoahGCSession session(request.cause, request.generation, true, + _degen_point == ShenandoahGC::ShenandoahDegenPoint::_degenerated_outside_cycle); ShenandoahDegenGC gc(_degen_point, request.generation); gc.collect(request.cause); _degen_point = ShenandoahGC::_degenerated_unset; @@ -635,12 +640,6 @@ void ShenandoahGenerationalControlThread::service_stw_degenerated_cycle(const Sh if (request.generation->is_global()) { assert(_heap->old_generation()->task_queues()->is_empty(), "Unexpected old generation marking tasks"); assert(_heap->global_generation()->task_queues()->is_empty(), "Unexpected global generation marking tasks"); - } else { - assert(request.generation->is_young(), "Expected degenerated young cycle, if not global."); - ShenandoahOldGeneration* old = _heap->old_generation(); - if (old->is_bootstrapping()) { - old->transition_to(ShenandoahOldGeneration::MARKING); - } } } @@ -672,7 +671,7 @@ bool ShenandoahGenerationalControlThread::request_concurrent_gc(ShenandoahGenera // Cancel the old GC and wait for the control thread to start servicing the new request. log_info(gc)("Preempting old generation mark to allow %s GC", generation->name()); while (gc_mode() == servicing_old) { - ShenandoahHeap::heap()->cancel_gc(GCCause::_shenandoah_concurrent_gc); + _heap->cancel_gc(GCCause::_shenandoah_concurrent_gc); notify_control_thread(ml, GCCause::_shenandoah_concurrent_gc, generation); ml.wait(); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp index 6912750378e..ca15c6db443 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp @@ -133,3 +133,4 @@ void ShenandoahGenerationalEvacuationTask::evacuate_and_promote_regions() { } } } + diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp index 27e374a0f85..1694121b955 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp @@ -346,26 +346,23 @@ oop ShenandoahGenerationalHeap::try_evacuate_object(oop p, Thread* thread, uint increase_object_age(copy_val, from_region_age + 1); } + // Relativize stack chunks before publishing the copy. After the forwarding CAS, + // mutators can see the copy and thaw it via the fast path if flags == 0. We must + // relativize derived pointers and set gc_mode before that happens. Skip if the + // copy's mark word is already a forwarding pointer (another thread won the race + // and overwrote the original's header before we copied it). + if (!ShenandoahForwarding::is_forwarded(copy_val)) { + ContinuationGCSupport::relativize_stack_chunk(copy_val); + } + // Try to install the new forwarding pointer. oop result = ShenandoahForwarding::try_update_forwardee(p, copy_val); if (result == copy_val) { // Successfully evacuated. Our copy is now the public one! - - // This is necessary for virtual thread support. This uses the mark word without - // considering that it may now be a forwarding pointer (and could therefore crash). - // Secondarily, we do not want to spend cycles relativizing stack chunks for oops - // that lost the evacuation race (and will therefore not become visible). It is - // safe to do this on the public copy (this is also done during concurrent mark). - ContinuationGCSupport::relativize_stack_chunk(copy_val); - if (ShenandoahEvacTracking) { // Record that the evacuation succeeded evac_tracker()->end_evacuation(thread, size * HeapWordSize, FROM_GENERATION, TO_GENERATION); } - - if (TO_GENERATION == OLD_GENERATION) { - old_generation()->handle_evacuation(copy, size); - } } else { // Failed to evacuate. We need to deal with the object that is left behind. Since this // new allocation is certainly after TAMS, it will be considered live in the next cycle. @@ -409,11 +406,14 @@ template oop ShenandoahGenerationalHeap::try_evacuate_object(oop p, Thread* thread, uint from_region_age); template oop ShenandoahGenerationalHeap::try_evacuate_object(oop p, Thread* thread, uint from_region_age); +// Call this function at the end of a GC cycle in order to establish proper sizes of young and old reserves, +// setting the old-generation balance so that GC can perform the anticipated evacuations. +// // Make sure old-generation is large enough, but no larger than is necessary, to hold mixed evacuations // and promotions, if we anticipate either. Any deficit is provided by the young generation, subject to // mutator_xfer_limit, and any surplus is transferred to the young generation. mutator_xfer_limit is -// the maximum we're able to transfer from young to old. This is called at the end of GC, as we prepare -// for the idle span that precedes the next GC. +// the maximum we're able to transfer from young to old. The mutator_xfer_limit constrains the transfer +// of memory from young to old. It does not limit young reserves. void ShenandoahGenerationalHeap::compute_old_generation_balance(size_t mutator_xfer_limit, size_t old_trashed_regions, size_t young_trashed_regions) { shenandoah_assert_heaplocked(); @@ -465,11 +465,17 @@ void ShenandoahGenerationalHeap::compute_old_generation_balance(size_t mutator_x bound_on_old_reserve)); assert(mutator_xfer_limit <= young_available, "Cannot transfer (%zu) memory that is not available (%zu)", mutator_xfer_limit, young_available); - // Young reserves are to be taken out of the mutator_xfer_limit. - if (young_reserve > mutator_xfer_limit) { - young_reserve = mutator_xfer_limit; + + if (young_reserve > young_available) { + young_reserve = young_available; + } + // We allow young_reserve to exceed mutator_xfer_limit. Essentially, this means the GC is already behind the pace + // of mutator allocations, and we'll need to trigger the next GC as soon as possible. + if (mutator_xfer_limit > young_reserve) { + mutator_xfer_limit -= young_reserve; + } else { + mutator_xfer_limit = 0; } - mutator_xfer_limit -= young_reserve; // Decide how much old space we should reserve for a mixed collection size_t proposed_reserve_for_mixed = 0; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index 817f5e8dc47..4b01ea1cd52 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -1353,12 +1353,21 @@ oop ShenandoahHeap::try_evacuate_object(oop p, Thread* thread, ShenandoahHeapReg // Copy the object: Copy::aligned_disjoint_words(cast_from_oop(p), copy, size); - // Try to install the new forwarding pointer. oop copy_val = cast_to_oop(copy); + + // Relativize stack chunks before publishing the copy. After the forwarding CAS, + // mutators can see the copy and thaw it via the fast path if flags == 0. We must + // relativize derived pointers and set gc_mode before that happens. Skip if the + // copy's mark word is already a forwarding pointer (another thread won the race + // and overwrote the original's header before we copied it). + if (!ShenandoahForwarding::is_forwarded(copy_val)) { + ContinuationGCSupport::relativize_stack_chunk(copy_val); + } + + // Try to install the new forwarding pointer. oop result = ShenandoahForwarding::try_update_forwardee(p, copy_val); if (result == copy_val) { // Successfully evacuated. Our copy is now the public one! - ContinuationGCSupport::relativize_stack_chunk(copy_val); shenandoah_assert_correct(nullptr, copy_val); if (ShenandoahEvacTracking) { evac_tracker()->end_evacuation(thread, size * HeapWordSize, from_region->affiliation(), target_gen); @@ -1641,7 +1650,8 @@ void ShenandoahHeap::set_active_generation(ShenandoahGeneration* generation) { _active_generation = generation; } -void ShenandoahHeap::on_cycle_start(GCCause::Cause cause, ShenandoahGeneration* generation) { +void ShenandoahHeap::on_cycle_start(GCCause::Cause cause, ShenandoahGeneration* generation, + bool is_degenerated, bool is_out_of_cycle) { shenandoah_policy()->record_collection_cause(cause); const GCCause::Cause current = gc_cause(); @@ -1650,7 +1660,11 @@ void ShenandoahHeap::on_cycle_start(GCCause::Cause cause, ShenandoahGeneration* set_gc_cause(cause); - generation->heuristics()->record_cycle_start(); + if (is_degenerated) { + generation->heuristics()->record_degenerated_cycle_start(is_out_of_cycle); + } else { + generation->heuristics()->record_cycle_start(); + } } void ShenandoahHeap::on_cycle_end(ShenandoahGeneration* generation) { @@ -1946,6 +1960,26 @@ void ShenandoahHeap::heap_region_iterate(ShenandoahHeapRegionClosure* blk) const } } +class ShenandoahHeapRegionIteratorTask : public WorkerTask { +private: + ShenandoahRegionIterator _regions; + ShenandoahHeapRegionClosure* _closure; + +public: + ShenandoahHeapRegionIteratorTask(ShenandoahHeapRegionClosure* closure) + : WorkerTask("Shenandoah Heap Region Iterator") + , _closure(closure) {} + + void work(uint worker_id) override { + ShenandoahParallelWorkerSession worker_session(worker_id); + ShenandoahHeapRegion* region = _regions.next(); + while (region != nullptr) { + _closure->heap_region_do(region); + region = _regions.next(); + } + } +}; + class ShenandoahParallelHeapRegionTask : public WorkerTask { private: ShenandoahHeap* const _heap; @@ -2002,6 +2036,11 @@ void ShenandoahHeap::parallel_heap_region_iterate(ShenandoahHeapRegionClosure* b } } +void ShenandoahHeap::heap_region_iterator(ShenandoahHeapRegionClosure* closure) const { + ShenandoahHeapRegionIteratorTask task(closure); + workers()->run_task(&task); +} + class ShenandoahRendezvousHandshakeClosure : public HandshakeClosure { public: inline ShenandoahRendezvousHandshakeClosure(const char* name) : HandshakeClosure(name) {} diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp index d4604be0aec..bed26a093d0 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp @@ -298,6 +298,7 @@ public: void heap_region_iterate(ShenandoahHeapRegionClosure* blk) const; void parallel_heap_region_iterate(ShenandoahHeapRegionClosure* blk) const; + void heap_region_iterator(ShenandoahHeapRegionClosure* blk) const; inline ShenandoahMmuTracker* mmu_tracker() { return &_mmu_tracker; }; @@ -563,7 +564,7 @@ public: return _evac_tracker; } - void on_cycle_start(GCCause::Cause cause, ShenandoahGeneration* generation); + void on_cycle_start(GCCause::Cause cause, ShenandoahGeneration* generation, bool is_degenerated, bool is_out_of_cycle); void on_cycle_end(ShenandoahGeneration* generation); ShenandoahVerifier* verifier(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp index 02f2beaf4e0..6d77cccaa6a 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp @@ -344,6 +344,8 @@ uint ShenandoahHeap::get_object_age(oop obj) { } if (w.has_monitor()) { w = w.monitor()->header(); + } else { + assert(!w.has_displaced_mark_helper(), "Mark word should not be displaced"); } assert(w.age() <= markWord::max_age, "Impossible!"); return w.age(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp index afc6b24e168..c031569b7c6 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp @@ -67,6 +67,7 @@ ShenandoahHeapRegion::ShenandoahHeapRegion(HeapWord* start, size_t index, bool c _new_top(nullptr), _empty_time(os::elapsedTime()), _top_before_promoted(nullptr), + _top_at_evac_start(start), _state(committed ? _empty_committed : _empty_uncommitted), _top(start), _tlab_allocs(0), @@ -565,25 +566,34 @@ void ShenandoahHeapRegion::recycle_internal() { assert(_recycling.is_set() && is_trash(), "Wrong state"); ShenandoahHeap* heap = ShenandoahHeap::heap(); + _top_at_evac_start = _bottom; _mixed_candidate_garbage_words = 0; - set_top(bottom()); clear_live_data(); reset_alloc_metadata(); heap->marking_context()->reset_top_at_mark_start(this); set_update_watermark(bottom()); + if (is_old()) { + heap->old_generation()->clear_cards_for(this); + } + if (ZapUnusedHeapArea) { SpaceMangler::mangle_region(MemRegion(bottom(), end())); } - - make_empty(); + set_top(bottom()); set_affiliation(FREE); + + // Lastly, set region state to empty + make_empty(); } // Upon return, this region has been recycled. We try to recycle it. // We may fail if some other thread recycled it before we do. void ShenandoahHeapRegion::try_recycle_under_lock() { shenandoah_assert_heaplocked(); - if (is_trash() && _recycling.try_set()) { + if (!is_trash()) { + return; + } + if (_recycling.try_set()) { if (is_trash()) { // At freeset rebuild time, which precedes recycling of collection set, we treat all cset regions as // part of capacity, as empty, as fully available, and as unaffiliated. This provides short-lived optimism @@ -603,6 +613,7 @@ void ShenandoahHeapRegion::try_recycle_under_lock() { os::naked_yield(); } } + assert(!is_trash(), "Must not"); } } @@ -610,7 +621,10 @@ void ShenandoahHeapRegion::try_recycle_under_lock() { // some GC worker thread has taken responsibility to recycle the region, eventually. void ShenandoahHeapRegion::try_recycle() { shenandoah_assert_not_heaplocked(); - if (is_trash() && _recycling.try_set()) { + if (!is_trash()) { + return; + } + if (_recycling.try_set()) { // Double check region state after win the race to set recycling flag if (is_trash()) { // At freeset rebuild time, which precedes recycling of collection set, we treat all cset regions as @@ -834,7 +848,7 @@ void ShenandoahHeapRegion::set_state(RegionState to) { evt.set_to(to); evt.commit(); } - _state.store_relaxed(to); + _state.release_store(to); } void ShenandoahHeapRegion::record_pin() { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp index 3a0ac042f57..e27bbbb737d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp @@ -218,7 +218,7 @@ public: bool is_alloc_allowed() const { auto cur_state = state(); return is_empty_state(cur_state) || cur_state == _regular || cur_state == _pinned; } bool is_stw_move_allowed() const { auto cur_state = state(); return cur_state == _regular || cur_state == _cset || (ShenandoahHumongousMoves && cur_state == _humongous_start); } - RegionState state() const { return _state.load_relaxed(); } + RegionState state() const { return _state.load_acquire(); } int state_ordinal() const { return region_state_to_ordinal(state()); } void record_pin(); @@ -246,6 +246,7 @@ private: double _empty_time; HeapWord* _top_before_promoted; + HeapWord* _top_at_evac_start; // Seldom updated fields Atomic _state; @@ -365,12 +366,15 @@ public: } // Returns true iff this region was promoted in place subsequent to the most recent start of concurrent old marking. - inline bool was_promoted_in_place() { + bool was_promoted_in_place() const { return _promoted_in_place; } inline void restore_top_before_promote(); inline size_t garbage_before_padded_for_promote() const; + HeapWord* get_top_at_evac_start() const { return _top_at_evac_start; } + void record_top_at_evac_start() { _top_at_evac_start = _top; } + // If next available memory is not aligned on address that is multiple of alignment, fill the empty space // so that returned object is aligned on an address that is a multiple of alignment_in_bytes. Requested // size is in words. It is assumed that this->is_old(). A pad object is allocated, filled, and registered diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionClosures.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionClosures.cpp index 3c6fe1a3df1..7554a9c9a2c 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionClosures.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionClosures.cpp @@ -80,6 +80,11 @@ void ShenandoahFinalMarkUpdateRegionStateClosure::heap_region_do(ShenandoahHeapR // Remember limit for updating refs. It's guaranteed that we get no // from-space-refs written from here on. r->set_update_watermark_at_safepoint(r->top()); + + if (r->is_old()) { + // Record where we need to start updating the remembered set + r->record_top_at_evac_start(); + } } else { assert(!r->has_live(), "Region %zu should have no live data", r->index()); assert(_ctx == nullptr || _ctx->top_at_mark_start(r) == r->top(), diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionCounters.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionCounters.cpp index ff1e3368e87..3e809ea84b9 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionCounters.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionCounters.cpp @@ -77,7 +77,8 @@ ShenandoahHeapRegionCounters::ShenandoahHeapRegionCounters() : } ShenandoahHeapRegionCounters::~ShenandoahHeapRegionCounters() { - if (_name_space != nullptr) FREE_C_HEAP_ARRAY(char, _name_space); + if (_name_space != nullptr) FREE_C_HEAP_ARRAY(_name_space); + if (_regions_data != nullptr) FREE_C_HEAP_ARRAY(_regions_data); } void ShenandoahHeapRegionCounters::write_snapshot(PerfLongVariable** regions, diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionSet.cpp index 560de816db9..1d2cd97b75d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionSet.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionSet.cpp @@ -47,7 +47,7 @@ ShenandoahHeapRegionSet::ShenandoahHeapRegionSet() : } ShenandoahHeapRegionSet::~ShenandoahHeapRegionSet() { - FREE_C_HEAP_ARRAY(jbyte, _set_map); + FREE_C_HEAP_ARRAY(_set_map); } void ShenandoahHeapRegionSet::add_region(ShenandoahHeapRegion* r) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahInPlacePromoter.cpp b/src/hotspot/share/gc/shenandoah/shenandoahInPlacePromoter.cpp index 10d61221e87..153193fa3a3 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahInPlacePromoter.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahInPlacePromoter.cpp @@ -238,6 +238,9 @@ void ShenandoahInPlacePromoter::promote(ShenandoahHeapRegion* region) const { // is_collector_free range. We'll add it to that range below. region->restore_top_before_promote(); + // We also need to record where those allocations begin so that we can later update the remembered set. + region->record_top_at_evac_start(); + assert(region->used() + pip_pad_bytes + pip_unpadded == region_size_bytes, "invariant"); // The update_watermark was likely established while we had the artificially high value of top. Make it sane now. diff --git a/src/hotspot/share/gc/shenandoah/shenandoahLock.hpp b/src/hotspot/share/gc/shenandoah/shenandoahLock.hpp index 7c91df191e5..5041419b2c7 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahLock.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahLock.hpp @@ -38,15 +38,19 @@ private: shenandoah_padding(0); Atomic _state; shenandoah_padding(1); +#ifdef ASSERT Atomic _owner; shenandoah_padding(2); +#endif template void contended_lock_internal(JavaThread* java_thread); static void yield_or_sleep(int &yields); public: - ShenandoahLock() : _state(unlocked), _owner(nullptr) {}; + ShenandoahLock() : _state(unlocked) { + DEBUG_ONLY(_owner.store_relaxed(nullptr);) + }; void lock(bool allow_block_for_safepoint = false) { assert(_owner.load_relaxed() != Thread::current(), "reentrant locking attempt, would deadlock"); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp index ba24e890769..0d42b95164b 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp @@ -77,10 +77,9 @@ void ShenandoahMark::do_task(ShenandoahObjToScanQueue* q, T* cl, ShenandoahLiveD if (task->is_not_chunked()) { if (obj->is_instance()) { // Case 1: Normal oop, process as usual. - if (ContinuationGCSupport::relativize_stack_chunk(obj)) { - // Loom doesn't support mixing of weak marking and strong marking of - // stack chunks. - cl->set_weak(false); + if (obj->is_stackChunk()) { + // Loom doesn't support mixing of weak marking and strong marking of stack chunks. + cl->set_weak(false); } obj->oop_iterate(cl); @@ -118,13 +117,11 @@ inline void ShenandoahMark::count_liveness(ShenandoahLiveData* live_data, oop ob // Age census for objects in the young generation if (GENERATION == YOUNG || (GENERATION == GLOBAL && region->is_young())) { assert(heap->mode()->is_generational(), "Only if generational"); - if (ShenandoahGenerationalAdaptiveTenuring) { - assert(region->is_young(), "Only for young objects"); - uint age = ShenandoahHeap::get_object_age(obj); - ShenandoahAgeCensus* const census = ShenandoahGenerationalHeap::heap()->age_census(); - CENSUS_NOISE(census->add(age, region->age(), region->youth(), size, worker_id);) - NO_CENSUS_NOISE(census->add(age, region->age(), size, worker_id);) - } + assert(region->is_young(), "Only for young objects"); + const uint age = ShenandoahHeap::get_object_age(obj); + ShenandoahAgeCensus* const census = ShenandoahGenerationalHeap::heap()->age_census(); + CENSUS_NOISE(census->add(age, region->age(), region->youth(), size, worker_id);) + NO_CENSUS_NOISE(census->add(age, region->age(), size, worker_id);) } if (!region->is_humongous_start()) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp index 594ad614d90..1b9532b3748 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp @@ -48,7 +48,7 @@ ShenandoahNMethod::ShenandoahNMethod(nmethod* nm, GrowableArray& oops, boo ShenandoahNMethod::~ShenandoahNMethod() { if (_oops != nullptr) { - FREE_C_HEAP_ARRAY(oop*, _oops); + FREE_C_HEAP_ARRAY(_oops); } } @@ -60,7 +60,7 @@ void ShenandoahNMethod::update() { detect_reloc_oops(nm(), oops, non_immediate_oops); if (oops.length() != _oops_count) { if (_oops != nullptr) { - FREE_C_HEAP_ARRAY(oop*, _oops); + FREE_C_HEAP_ARRAY(_oops); _oops = nullptr; } @@ -394,7 +394,7 @@ ShenandoahNMethodList::ShenandoahNMethodList(int size) : ShenandoahNMethodList::~ShenandoahNMethodList() { assert(_list != nullptr, "Sanity"); assert(_ref_count == 0, "Must be"); - FREE_C_HEAP_ARRAY(ShenandoahNMethod*, _list); + FREE_C_HEAP_ARRAY(_list); } void ShenandoahNMethodList::transfer(ShenandoahNMethodList* const list, int limit) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahNumberSeq.cpp b/src/hotspot/share/gc/shenandoah/shenandoahNumberSeq.cpp index 1ddd8e1c032..82b027faca2 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahNumberSeq.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahNumberSeq.cpp @@ -39,10 +39,10 @@ HdrSeq::~HdrSeq() { for (int c = 0; c < MagBuckets; c++) { int* sub = _hdr[c]; if (sub != nullptr) { - FREE_C_HEAP_ARRAY(int, sub); + FREE_C_HEAP_ARRAY(sub); } } - FREE_C_HEAP_ARRAY(int*, _hdr); + FREE_C_HEAP_ARRAY(_hdr); } void HdrSeq::add(double val) { @@ -191,7 +191,7 @@ BinaryMagnitudeSeq::BinaryMagnitudeSeq() { } BinaryMagnitudeSeq::~BinaryMagnitudeSeq() { - FREE_C_HEAP_ARRAY(size_t, _mags); + FREE_C_HEAP_ARRAY(_mags); } void BinaryMagnitudeSeq::clear() { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp index 16a24da8b1c..37de5966554 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp @@ -114,7 +114,7 @@ ShenandoahOldGeneration::ShenandoahOldGeneration(uint max_queues) _promotable_regular_regions(0), _is_parsable(true), _card_scan(nullptr), - _state(WAITING_FOR_BOOTSTRAP), + _state(IDLE), _growth_percent_before_collection(INITIAL_GROWTH_PERCENT_BEFORE_COLLECTION) { assert(type() == ShenandoahGenerationType::OLD, "OO sanity"); @@ -318,6 +318,11 @@ void ShenandoahOldGeneration::heap_region_iterate(ShenandoahHeapRegionClosure* c ShenandoahHeap::heap()->heap_region_iterate(&old_regions_cl); } +void ShenandoahOldGeneration::heap_region_iterator(ShenandoahHeapRegionClosure* cl) { + ShenandoahIncludeRegionClosure old_regions_cl(cl); + ShenandoahHeap::heap()->heap_region_iterator(&old_regions_cl); +} + void ShenandoahOldGeneration::set_concurrent_mark_in_progress(bool in_progress) { ShenandoahHeap::heap()->set_concurrent_old_mark_in_progress(in_progress); } @@ -326,6 +331,12 @@ bool ShenandoahOldGeneration::is_concurrent_mark_in_progress() { return ShenandoahHeap::heap()->is_concurrent_old_mark_in_progress(); } +void ShenandoahOldGeneration::record_tops_at_evac_start() { + for_each_region([](ShenandoahHeapRegion* region) { + region->record_top_at_evac_start(); + }); +} + void ShenandoahOldGeneration::cancel_marking() { if (is_concurrent_mark_in_progress()) { log_debug(gc)("Abandon SATB buffers"); @@ -339,7 +350,7 @@ void ShenandoahOldGeneration::cancel_gc() { shenandoah_assert_safepoint(); if (is_idle()) { #ifdef ASSERT - validate_waiting_for_bootstrap(); + validate_idle(); #endif } else { log_info(gc)("Terminating old gc cycle."); @@ -350,7 +361,7 @@ void ShenandoahOldGeneration::cancel_gc() { // Remove old generation access to young generation mark queues ShenandoahHeap::heap()->young_generation()->set_old_gen_task_queues(nullptr); // Transition to IDLE now. - transition_to(ShenandoahOldGeneration::WAITING_FOR_BOOTSTRAP); + transition_to(ShenandoahOldGeneration::IDLE); } } @@ -477,9 +488,8 @@ void ShenandoahOldGeneration::prepare_regions_and_collection_set(bool concurrent const char* ShenandoahOldGeneration::state_name(State state) { switch (state) { - case WAITING_FOR_BOOTSTRAP: return "Waiting for Bootstrap"; + case IDLE: return "Idle"; case FILLING: return "Coalescing"; - case BOOTSTRAPPING: return "Bootstrapping"; case MARKING: return "Marking"; case EVACUATING: return "Evacuating"; case EVACUATING_AFTER_GLOBAL: return "Evacuating (G)"; @@ -517,7 +527,7 @@ void ShenandoahOldGeneration::transition_to(State new_state) { // the old generation in the respective states (EVACUATING or FILLING). After a Full GC, // the mark bitmaps are all reset, all regions are parsable and the mark context will // not be "complete". After a Full GC, remembered set scans will _not_ use the mark bitmap -// and we expect the old generation to be waiting for bootstrap. +// and we expect the old generation to be idle. // // +-----------------+ // +------------> | FILLING | <---+ @@ -526,19 +536,12 @@ void ShenandoahOldGeneration::transition_to(State new_state) { // | | | | // | | | Filling Complete | <-> A global collection may // | | v | move the old generation -// | | +-----------------+ | directly from waiting for -// +-- |-- |--------> | WAITING | | bootstrap to filling or -// | | | +---- | FOR BOOTSTRAP | ----+ evacuating. It may also -// | | | | +-----------------+ move from filling to waiting -// | | | | | for bootstrap. -// | | | | | Reset Bitmap -// | | | | v -// | | | | +-----------------+ +----------------------+ -// | | | | | BOOTSTRAP | <-> | YOUNG GC | -// | | | | | | | (RSet Parses Region) | -// | | | | +-----------------+ +----------------------+ +// | | +-----------------+ | directly from idle to +// +-- |-- |--------> | IDLE | | filling or evacuating. +// | | | +---- | | ----+ It may also move from +// | | | | +-----------------+ filling to idle. // | | | | | -// | | | | | Old Marking +// | | | | | Reset Bitmap + Start Marking // | | | | v // | | | | +-----------------+ +----------------------+ // | | | | | MARKING | <-> | YOUNG GC | @@ -564,29 +567,23 @@ void ShenandoahOldGeneration::validate_transition(State new_state) { ShenandoahGenerationalHeap* heap = ShenandoahGenerationalHeap::heap(); switch (new_state) { case FILLING: - assert(_state != BOOTSTRAPPING, "Cannot begin making old regions parsable after bootstrapping"); assert(is_mark_complete(), "Cannot begin filling without first completing marking, state is '%s'", state_name(_state)); assert(_old_heuristics->has_coalesce_and_fill_candidates(), "Cannot begin filling without something to fill."); break; - case WAITING_FOR_BOOTSTRAP: + case IDLE: // GC cancellation can send us back here from any state. - validate_waiting_for_bootstrap(); - break; - case BOOTSTRAPPING: - assert(_state == WAITING_FOR_BOOTSTRAP, "Cannot reset bitmap without making old regions parsable, state is '%s'", state_name(_state)); - assert(_old_heuristics->unprocessed_old_collection_candidates() == 0, "Cannot bootstrap with mixed collection candidates"); - assert(!heap->is_prepare_for_old_mark_in_progress(), "Cannot still be making old regions parsable."); + validate_idle(); break; case MARKING: - assert(_state == BOOTSTRAPPING, "Must have finished bootstrapping before marking, state is '%s'", state_name(_state)); - assert(heap->young_generation()->old_gen_task_queues() != nullptr, "Young generation needs old mark queues."); - assert(heap->is_concurrent_old_mark_in_progress(), "Should be marking old now."); + assert(_state == IDLE, "Must be idle before marking, state is '%s'", state_name(_state)); + assert(_old_heuristics->unprocessed_old_collection_candidates() == 0, "Cannot start marking with mixed collection candidates"); + assert(!heap->is_prepare_for_old_mark_in_progress(), "Cannot still be making old regions parsable."); break; case EVACUATING_AFTER_GLOBAL: assert(_state == EVACUATING, "Must have been evacuating, state is '%s'", state_name(_state)); break; case EVACUATING: - assert(_state == WAITING_FOR_BOOTSTRAP || _state == MARKING, "Cannot have old collection candidates without first marking, state is '%s'", state_name(_state)); + assert(_state == IDLE || _state == MARKING, "Cannot have old collection candidates without first marking, state is '%s'", state_name(_state)); assert(_old_heuristics->unprocessed_old_collection_candidates() > 0, "Must have collection candidates here."); break; default: @@ -594,10 +591,10 @@ void ShenandoahOldGeneration::validate_transition(State new_state) { } } -bool ShenandoahOldGeneration::validate_waiting_for_bootstrap() { +bool ShenandoahOldGeneration::validate_idle() { ShenandoahHeap* heap = ShenandoahHeap::heap(); - assert(!heap->is_concurrent_old_mark_in_progress(), "Cannot become ready for bootstrap during old mark."); - assert(heap->young_generation()->old_gen_task_queues() == nullptr, "Cannot become ready for bootstrap when still setup for bootstrapping."); + assert(!heap->is_concurrent_old_mark_in_progress(), "Cannot be idle during old mark."); + assert(heap->young_generation()->old_gen_task_queues() == nullptr, "Cannot be idle when still setup for bootstrapping."); assert(!is_concurrent_mark_in_progress(), "Cannot be marking in IDLE"); assert(!heap->young_generation()->is_bootstrap_cycle(), "Cannot have old mark queues if IDLE"); assert(!_old_heuristics->has_coalesce_and_fill_candidates(), "Cannot have coalesce and fill candidates in IDLE"); @@ -676,15 +673,19 @@ void ShenandoahOldGeneration::log_failed_promotion(LogStream& ls, Thread* thread } } -void ShenandoahOldGeneration::handle_evacuation(HeapWord* obj, size_t words) const { - // Only register the copy of the object that won the evacuation race. - _card_scan->register_object_without_lock(obj); - - // Mark the entire range of the evacuated object as dirty. At next remembered set scan, - // we will clear dirty bits that do not hold interesting pointers. It's more efficient to - // do this in batch, in a background GC thread than to try to carefully dirty only cards - // that hold interesting pointers right now. - _card_scan->mark_range_as_dirty(obj, words); +void ShenandoahOldGeneration::update_card_table() { + for_each_region([this](ShenandoahHeapRegion* region) { + if (region->is_regular()) { + // Humongous regions are promoted in place, remembered set maintenance is handled there + // Regular regions that are promoted in place have their rset maintenance handled for + // the objects in the region when it was promoted. We record TEAS for such a region + // when the in-place-promotion is completed. Such a region may be used for additional + // promotions in the same cycle it was itself promoted. + if (region->top() > region->get_top_at_evac_start()) { + _card_scan->update_card_table(region->get_top_at_evac_start(), region->top()); + } + } + }); } bool ShenandoahOldGeneration::has_unprocessed_collection_candidates() { @@ -699,7 +700,7 @@ void ShenandoahOldGeneration::abandon_collection_candidates() { _old_heuristics->abandon_collection_candidates(); } -void ShenandoahOldGeneration::prepare_for_mixed_collections_after_global_gc() { +void ShenandoahOldGeneration::transition_old_generation_after_global_gc() { assert(is_mark_complete(), "Expected old generation mark to be complete after global cycle."); _old_heuristics->prepare_for_old_collections(); log_info(gc, ergo)("After choosing global collection set, mixed candidates: " UINT32_FORMAT ", coalescing candidates: %zu", @@ -733,7 +734,7 @@ void ShenandoahOldGeneration::set_parsable(bool parsable) { // that we would unload classes and make everything parsable. But, we know // that now so we can override this state. abandon_collection_candidates(); - transition_to(ShenandoahOldGeneration::WAITING_FOR_BOOTSTRAP); + transition_to(ShenandoahOldGeneration::IDLE); break; default: // We can get here during a full GC. The full GC will cancel anything @@ -750,7 +751,7 @@ void ShenandoahOldGeneration::complete_mixed_evacuations() { assert(is_doing_mixed_evacuations(), "Mixed evacuations should be in progress"); if (!_old_heuristics->has_coalesce_and_fill_candidates()) { // No candidate regions to coalesce and fill - transition_to(ShenandoahOldGeneration::WAITING_FOR_BOOTSTRAP); + transition_to(ShenandoahOldGeneration::IDLE); return; } @@ -764,7 +765,7 @@ void ShenandoahOldGeneration::complete_mixed_evacuations() { // more to do. assert(state() == ShenandoahOldGeneration::EVACUATING_AFTER_GLOBAL, "Should be evacuating after a global cycle"); abandon_collection_candidates(); - transition_to(ShenandoahOldGeneration::WAITING_FOR_BOOTSTRAP); + transition_to(ShenandoahOldGeneration::IDLE); } void ShenandoahOldGeneration::abandon_mixed_evacuations() { @@ -774,7 +775,7 @@ void ShenandoahOldGeneration::abandon_mixed_evacuations() { break; case ShenandoahOldGeneration::EVACUATING_AFTER_GLOBAL: abandon_collection_candidates(); - transition_to(ShenandoahOldGeneration::WAITING_FOR_BOOTSTRAP); + transition_to(ShenandoahOldGeneration::IDLE); break; default: log_warning(gc)("Abandon mixed evacuations in unexpected state: %s", state_name(state())); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp index 12e046a1afc..942f93c5c68 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp @@ -172,8 +172,8 @@ public: void handle_failed_promotion(Thread* thread, size_t size) const; void log_failed_promotion(LogStream& ls, Thread* thread, size_t size) const; - // A successful evacuation re-dirties the cards and registers the object with the remembered set - void handle_evacuation(HeapWord* obj, size_t words) const; + // Iterate over recently promoted objects to update card table and object registrations + void update_card_table(); // Clear the flag after it is consumed by the control thread bool clear_failed_evacuation() { @@ -199,11 +199,36 @@ public: // Mark card for this location as dirty void mark_card_as_dirty(void* location); + template + class ShenandoahHeapRegionLambda : public ShenandoahHeapRegionClosure { + T _region_lambda; + public: + explicit ShenandoahHeapRegionLambda(T region_lambda) : _region_lambda(region_lambda) {} + + void heap_region_do(ShenandoahHeapRegion* r) override { + _region_lambda(r); + } + + bool is_thread_safe() override { + return true; + } + + size_t parallel_region_stride() override { + // Temporarily override to force parallelism when updating card table + return 8; + } + }; + + template + void for_each_region(LambdaT lambda) { + ShenandoahHeapRegionLambda l(lambda); + heap_region_iterator(&l); + } + void parallel_heap_region_iterate(ShenandoahHeapRegionClosure* cl) override; - void parallel_heap_region_iterate_free(ShenandoahHeapRegionClosure* cl) override; - void heap_region_iterate(ShenandoahHeapRegionClosure* cl) override; + void heap_region_iterator(ShenandoahHeapRegionClosure* cl); bool contains(ShenandoahAffiliation affiliation) const override; bool contains(ShenandoahHeapRegion* region) const override; @@ -212,8 +237,17 @@ public: void set_concurrent_mark_in_progress(bool in_progress) override; bool is_concurrent_mark_in_progress() override; + // For old regions, objects between top at evac start and top represent promoted objects. + // These objects will need to have their cards dirtied and their offsets within the cards registered. + void record_tops_at_evac_start(); + bool entry_coalesce_and_fill(); - void prepare_for_mixed_collections_after_global_gc(); + + // Global collections touch old regions, so the old generation needs to be informed of this. + // The old generation may decide to schedule additional mixed collections, or may decide to + // immediately coalesce-and-fill old objects in regions that were not collected. + void transition_old_generation_after_global_gc(); + void prepare_gc() override; void prepare_regions_and_collection_set(bool concurrent) override; void record_success_concurrent(bool abbreviated) override; @@ -256,11 +290,7 @@ public: } bool is_idle() const { - return state() == WAITING_FOR_BOOTSTRAP; - } - - bool is_bootstrapping() const { - return state() == BOOTSTRAPPING; + return state() == IDLE; } // Amount of live memory (bytes) in regions waiting for mixed collections @@ -271,11 +301,11 @@ public: public: enum State { - FILLING, WAITING_FOR_BOOTSTRAP, BOOTSTRAPPING, MARKING, EVACUATING, EVACUATING_AFTER_GLOBAL + FILLING, IDLE, MARKING, EVACUATING, EVACUATING_AFTER_GLOBAL }; #ifdef ASSERT - bool validate_waiting_for_bootstrap(); + bool validate_idle(); #endif private: @@ -318,7 +348,7 @@ public: size_t usage_trigger_threshold() const; bool can_start_gc() { - return _state == WAITING_FOR_BOOTSTRAP; + return _state == IDLE; } static const char* state_name(State state); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahPLAB.cpp b/src/hotspot/share/gc/shenandoah/shenandoahPLAB.cpp index 412cfa9447e..5049113b665 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahPLAB.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahPLAB.cpp @@ -210,12 +210,4 @@ void ShenandoahPLAB::retire() { // plab->retire() overwrites unused memory between plab->top() and plab->hard_end() with a dummy object to make memory parsable. // It adds the size of this unused memory, in words, to plab->waste(). _plab->retire(); - if (top != nullptr && _plab->waste() > original_waste && _heap->is_in_old(top)) { - // If retiring the plab created a filler object, then we need to register it with our card scanner so it can - // safely walk the region backing the plab. - log_debug(gc, plab)("retire_plab() is registering remnant of size %zu at " PTR_FORMAT, - (_plab->waste() - original_waste) * HeapWordSize, p2i(top)); - // No lock is necessary because the PLAB memory is aligned on card boundaries. - _heap->old_generation()->card_scan()->register_object_without_lock(top); - } } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp b/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp index e890008b916..6a316e2265a 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp @@ -109,10 +109,11 @@ class outputStream; f(conc_strong_roots, "Concurrent Strong Roots") \ SHENANDOAH_PAR_PHASE_DO(conc_strong_roots_, " CSR: ", f) \ f(conc_evac, "Concurrent Evacuation") \ + f(conc_update_card_table, "Concurrent Update Cards") \ f(conc_final_roots, "Concurrent Final Roots") \ f(promote_in_place, " Promote Regions") \ - f(final_roots_gross, "Pause Verify Final Roots (G)") \ - f(final_roots, "Pause Verify Final Roots (N)") \ + f(final_verify_gross, "Pause Final Verify (G)") \ + f(final_verify, "Pause Final Verify (N)") \ \ f(init_update_refs_gross, "Pause Init Update Refs (G)") \ f(init_update_refs, "Pause Init Update Refs (N)") \ @@ -254,7 +255,7 @@ public: void flush_cycle_to_global(); static const char* phase_name(Phase phase) { - assert(phase >= 0 && phase < _num_phases, "Out of bound"); + assert(phase >= 0 && phase < _num_phases, "Out of bounds: %d", phase); return _phase_names[phase]; } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp b/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp index 7b68e6ac625..80825ac43ad 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp @@ -200,9 +200,6 @@ ShenandoahRootAdjuster::ShenandoahRootAdjuster(uint n_workers, ShenandoahPhaseTi void ShenandoahRootAdjuster::roots_do(uint worker_id, OopClosure* oops) { NMethodToOopClosure code_blob_cl(oops, NMethodToOopClosure::FixRelocations); ShenandoahNMethodAndDisarmClosure nmethods_and_disarm_Cl(oops); - NMethodToOopClosure* adjust_code_closure = ShenandoahCodeRoots::use_nmethod_barriers_for_mark() ? - static_cast(&nmethods_and_disarm_Cl) : - static_cast(&code_blob_cl); CLDToOopClosure adjust_cld_closure(oops, ClassLoaderData::_claim_strong); // Process light-weight/limited parallel roots then @@ -211,7 +208,7 @@ void ShenandoahRootAdjuster::roots_do(uint worker_id, OopClosure* oops) { _cld_roots.cld_do(&adjust_cld_closure, worker_id); // Process heavy-weight/fully parallel roots the last - _code_roots.nmethods_do(adjust_code_closure, worker_id); + _code_roots.nmethods_do(&nmethods_and_disarm_Cl, worker_id); _thread_roots.oops_do(oops, nullptr, worker_id); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.inline.hpp index 6aebec28163..4504ac96819 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.inline.hpp @@ -172,10 +172,6 @@ template void ShenandoahRootUpdater::roots_do(uint worker_id, IsAlive* is_alive, KeepAlive* keep_alive) { NMethodToOopClosure update_nmethods(keep_alive, NMethodToOopClosure::FixRelocations); ShenandoahNMethodAndDisarmClosure nmethods_and_disarm_Cl(keep_alive); - NMethodToOopClosure* codes_cl = ShenandoahCodeRoots::use_nmethod_barriers_for_mark() ? - static_cast(&nmethods_and_disarm_Cl) : - static_cast(&update_nmethods); - CLDToOopClosure clds(keep_alive, ClassLoaderData::_claim_strong); // Process light-weight/limited parallel roots then @@ -184,7 +180,7 @@ void ShenandoahRootUpdater::roots_do(uint worker_id, IsAlive* is_alive, KeepAliv _cld_roots.cld_do(&clds, worker_id); // Process heavy-weight/fully parallel roots the last - _code_roots.nmethods_do(codes_cl, worker_id); + _code_roots.nmethods_do(&nmethods_and_disarm_Cl, worker_id); _thread_roots.oops_do(keep_alive, nullptr, worker_id); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahSTWMark.cpp b/src/hotspot/share/gc/shenandoah/shenandoahSTWMark.cpp index 117984a6d41..5b4ce6d0bc9 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahSTWMark.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahSTWMark.cpp @@ -74,7 +74,7 @@ void ShenandoahSTWMark::mark() { // Arm all nmethods. Even though this is STW mark, some marking code // piggybacks on nmethod barriers for special instances. - ShenandoahCodeRoots::arm_nmethods_for_mark(); + ShenandoahCodeRoots::arm_nmethods(); // Weak reference processing ShenandoahReferenceProcessor* rp = _generation->ref_processor(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp index 9e160d5b294..8d7ba2dc46f 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp @@ -31,6 +31,33 @@ #include "logging/log.hpp" #include "runtime/threads.hpp" +// A closure that takes an oop in the old generation and, if it's pointing +// into the young generation, dirties the corresponding remembered set entry. +class ShenandoahDirtyRememberedSetClosure : public BasicOopIterateClosure { +protected: + ShenandoahGenerationalHeap* const _heap; + ShenandoahScanRemembered* const _scanner; + +public: + ShenandoahDirtyRememberedSetClosure() : + _heap(ShenandoahGenerationalHeap::heap()), + _scanner(_heap->old_generation()->card_scan()) {} + + template + void work(T* p) { + assert(_heap->is_in_old(p), "Expecting to get an old gen address"); + if (T o = RawAccess<>::oop_load(p); !CompressedOops::is_null(o)) { + if (const oop obj = CompressedOops::decode_not_null(o); _heap->is_in_young(obj)) { + // Dirty the card containing the cross-generational pointer. + _scanner->mark_card_as_dirty((HeapWord*) p); + } + } + } + + void do_oop(narrowOop* p) override { work(p); } + void do_oop(oop* p) override { work(p); } +}; + size_t ShenandoahDirectCardMarkRememberedSet::last_valid_index() const { return _card_table->last_valid_index(); } @@ -161,7 +188,6 @@ void ShenandoahCardCluster::register_object_without_lock(HeapWord* address) { uint8_t offset_in_card = checked_cast(pointer_delta(address, card_start_address)); if (!starts_object(card_at_start)) { - set_starts_object_bit(card_at_start); set_first_start(card_at_start, offset_in_card); set_last_start(card_at_start, offset_in_card); } else { @@ -172,6 +198,49 @@ void ShenandoahCardCluster::register_object_without_lock(HeapWord* address) { } } +void ShenandoahCardCluster::update_card_table(HeapWord* start, HeapWord* end) { + HeapWord* address = start; + HeapWord* previous_address = nullptr; + uint8_t previous_offset = 0; + size_t previous_card_index = -1; + ShenandoahDirtyRememberedSetClosure make_cards_dirty; + + log_debug(gc, remset)("Update remembered set from " PTR_FORMAT ", to " PTR_FORMAT, p2i(start), p2i(end)); + _rs->mark_range_as_dirty(start, pointer_delta(end, start)); + + while (address < end) { + + // Compute card and offset in card for this object + const size_t object_card_index = _rs->card_index_for_addr(address); + const HeapWord* card_start_address = _rs->addr_for_card_index(object_card_index); + const uint8_t offset_in_card = checked_cast(pointer_delta(address, card_start_address)); + + if (object_card_index != previous_card_index) { + if (previous_address != nullptr) { + // Register the previous object on the previous card, we are starting a new card here + set_last_start(previous_card_index, previous_offset); + } + + previous_card_index = object_card_index; + if (!starts_object(object_card_index)) { + // The previous cycle may have recorded an earlier start in this card. Do not overwrite it. + set_first_start(object_card_index, offset_in_card); + } + } + + previous_offset = offset_in_card; + previous_address = address; + + const oop obj = cast_to_oop(address); + address += obj->size(); + } + + // Register the last object seen in this range. + if (previous_address != nullptr) { + set_last_start(previous_card_index, previous_offset); + } +} + void ShenandoahCardCluster::coalesce_objects(HeapWord* address, size_t length_in_words) { size_t card_at_start = _rs->card_index_for_addr(address); @@ -641,36 +710,6 @@ void ShenandoahScanRemembered::merge_worker_card_stats_cumulative( } #endif -// A closure that takes an oop in the old generation and, if it's pointing -// into the young generation, dirties the corresponding remembered set entry. -// This is only used to rebuild the remembered set after a full GC. -class ShenandoahDirtyRememberedSetClosure : public BasicOopIterateClosure { -protected: - ShenandoahGenerationalHeap* const _heap; - ShenandoahScanRemembered* const _scanner; - -public: - ShenandoahDirtyRememberedSetClosure() : - _heap(ShenandoahGenerationalHeap::heap()), - _scanner(_heap->old_generation()->card_scan()) {} - - template - inline void work(T* p) { - assert(_heap->is_in_old(p), "Expecting to get an old gen address"); - T o = RawAccess<>::oop_load(p); - if (!CompressedOops::is_null(o)) { - oop obj = CompressedOops::decode_not_null(o); - if (_heap->is_in_young(obj)) { - // Dirty the card containing the cross-generational pointer. - _scanner->mark_card_as_dirty((HeapWord*) p); - } - } - } - - virtual void do_oop(narrowOop* p) { work(p); } - virtual void do_oop(oop* p) { work(p); } -}; - ShenandoahDirectCardMarkRememberedSet::ShenandoahDirectCardMarkRememberedSet(ShenandoahCardTable* card_table, size_t total_card_count) : LogCardValsPerIntPtr(log2i_exact(sizeof(intptr_t)) - log2i_exact(sizeof(CardValue))), LogCardSizeInWords(log2i_exact(CardTable::card_size_in_words())) { @@ -1039,38 +1078,44 @@ void ShenandoahReconstructRememberedSetTask::work(uint worker_id) { ShenandoahDirtyRememberedSetClosure dirty_cards_for_cross_generational_pointers; while (r != nullptr) { - if (r->is_old() && r->is_active()) { - HeapWord* obj_addr = r->bottom(); - if (r->is_humongous_start()) { - // First, clear the remembered set - oop obj = cast_to_oop(obj_addr); - size_t size = obj->size(); - - size_t num_regions = ShenandoahHeapRegion::required_regions(size * HeapWordSize); - size_t region_index = r->index(); - ShenandoahHeapRegion* humongous_region = heap->get_region(region_index); - while (num_regions-- != 0) { - scanner->reset_object_range(humongous_region->bottom(), humongous_region->end()); - region_index++; - humongous_region = heap->get_region(region_index); - } - - // Then register the humongous object and DIRTY relevant remembered set cards - scanner->register_object_without_lock(obj_addr); - obj->oop_iterate(&dirty_cards_for_cross_generational_pointers); - } else if (!r->is_humongous()) { - scanner->reset_object_range(r->bottom(), r->end()); - - // Then iterate over all objects, registering object and DIRTYing relevant remembered set cards - HeapWord* t = r->top(); - while (obj_addr < t) { + if (r->is_active()) { + if (r->is_old()) { + HeapWord* obj_addr = r->bottom(); + if (r->is_humongous_start()) { + // First, clear the remembered set oop obj = cast_to_oop(obj_addr); + size_t size = obj->size(); + + size_t num_regions = ShenandoahHeapRegion::required_regions(size * HeapWordSize); + size_t region_index = r->index(); + ShenandoahHeapRegion* humongous_region = heap->get_region(region_index); + while (num_regions-- != 0) { + scanner->reset_object_range(humongous_region->bottom(), humongous_region->end()); + region_index++; + humongous_region = heap->get_region(region_index); + } + + // Then register the humongous object and DIRTY relevant remembered set cards scanner->register_object_without_lock(obj_addr); - obj_addr += obj->oop_iterate_size(&dirty_cards_for_cross_generational_pointers); - } - } // else, ignore humongous continuation region + obj->oop_iterate(&dirty_cards_for_cross_generational_pointers); + } else if (!r->is_humongous()) { + scanner->reset_object_range(r->bottom(), r->end()); + + // Then iterate over all objects, registering object and DIRTYing relevant remembered set cards + HeapWord* t = r->top(); + while (obj_addr < t) { + oop obj = cast_to_oop(obj_addr); + scanner->register_object_without_lock(obj_addr); + obj_addr += obj->oop_iterate_size(&dirty_cards_for_cross_generational_pointers); + } + } // else, ignore humongous continuation region + } else { + // The region is young, but it may become old again and we don't want stale remembered set data. + assert(r->is_young(), "Region: %zu, is active but free", r->index()); + heap->old_generation()->clear_cards_for(r); + } } - // else, this region is FREE or YOUNG or inactive and we can ignore it. + // else, this region is FREE or inactive and we can ignore it. r = _regions->next(); } } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.hpp b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.hpp index c2c117e86e6..244ed7edd4c 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.hpp @@ -413,7 +413,7 @@ public: } ~ShenandoahCardCluster() { - FREE_C_HEAP_ARRAY(crossing_info, _object_starts); + FREE_C_HEAP_ARRAY(_object_starts); _object_starts = nullptr; } @@ -603,6 +603,9 @@ public: // as address. void register_object_without_lock(HeapWord* address); + // Dirty cards and register objects for the given range in memory. + void update_card_table(HeapWord* start, HeapWord* end); + // During the reference updates phase of GC, we walk through each old-gen memory region that was // not part of the collection set and we invalidate all unmarked objects. As part of this effort, // we coalesce neighboring dead objects in order to make future remembered set scanning more @@ -748,7 +751,7 @@ public: for (uint i = 0; i < ParallelGCThreads; i++) { delete _card_stats[i]; } - FREE_C_HEAP_ARRAY(HdrSeq*, _card_stats); + FREE_C_HEAP_ARRAY(_card_stats); _card_stats = nullptr; } assert(_card_stats == nullptr, "Error"); @@ -814,6 +817,10 @@ public: } } + void update_card_table(HeapWord* start, HeapWord* end) const { + _scc->update_card_table(start, end); + } + // Return true iff this object is "properly" registered. bool verify_registration(HeapWord* address, ShenandoahMarkingContext* ctx); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahSimpleBitMap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahSimpleBitMap.cpp index 82a759e34db..d0029b60167 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahSimpleBitMap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahSimpleBitMap.cpp @@ -35,7 +35,7 @@ ShenandoahSimpleBitMap::ShenandoahSimpleBitMap(idx_t num_bits) : ShenandoahSimpleBitMap::~ShenandoahSimpleBitMap() { if (_bitmap != nullptr) { - FREE_C_HEAP_ARRAY(uintx, _bitmap); + FREE_C_HEAP_ARRAY(_bitmap); } } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahTrace.cpp b/src/hotspot/share/gc/shenandoah/shenandoahTrace.cpp index bbb44348355..c28e572dd6b 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahTrace.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahTrace.cpp @@ -27,9 +27,7 @@ #include "jfr/jfrEvents.hpp" void ShenandoahTracer::report_evacuation_info(const ShenandoahCollectionSet* cset, - size_t free_regions, size_t regions_promoted_humongous, size_t regions_promoted_regular, - size_t regular_promoted_garbage, size_t regular_promoted_free, size_t regions_immediate, - size_t immediate_size) { + size_t free_regions, size_t regions_immediate, size_t immediate_size) { EventShenandoahEvacuationInformation e; if (e.should_commit()) { @@ -37,13 +35,6 @@ void ShenandoahTracer::report_evacuation_info(const ShenandoahCollectionSet* cse e.set_cSetRegions(cset->count()); e.set_cSetUsedBefore(cset->used()); e.set_cSetUsedAfter(cset->live()); - e.set_collectedOld(cset->get_live_bytes_in_old_regions()); - e.set_collectedPromoted(cset->get_live_bytes_in_tenurable_regions()); - e.set_collectedYoung(cset->get_live_bytes_in_untenurable_regions()); - e.set_regionsPromotedHumongous(regions_promoted_humongous); - e.set_regionsPromotedRegular(regions_promoted_regular); - e.set_regularPromotedGarbage(regular_promoted_garbage); - e.set_regularPromotedFree(regular_promoted_free); e.set_freeRegions(free_regions); e.set_regionsImmediate(regions_immediate); e.set_immediateBytes(immediate_size); @@ -51,3 +42,24 @@ void ShenandoahTracer::report_evacuation_info(const ShenandoahCollectionSet* cse e.commit(); } } + +void ShenandoahTracer::report_promotion_info(const ShenandoahCollectionSet* cset, + size_t regions_promoted_humongous, size_t humongous_promoted_garbage, size_t humongous_promoted_free, + size_t regions_promoted_regular, size_t regular_promoted_garbage, size_t regular_promoted_free) { + + EventShenandoahPromotionInformation e; + if (e.should_commit()) { + e.set_gcId(GCId::current()); + e.set_collectedOld(cset->get_live_bytes_in_old_regions()); + e.set_collectedPromoted(cset->get_live_bytes_in_tenurable_regions()); + e.set_collectedYoung(cset->get_live_bytes_in_untenurable_regions()); + e.set_regionsPromotedHumongous(regions_promoted_humongous); + e.set_humongousPromotedGarbage(humongous_promoted_garbage); + e.set_humongousPromotedFree(humongous_promoted_free); + e.set_regionsPromotedRegular(regions_promoted_regular); + e.set_regularPromotedGarbage(regular_promoted_garbage); + e.set_regularPromotedFree(regular_promoted_free); + + e.commit(); + } +} diff --git a/src/hotspot/share/gc/shenandoah/shenandoahTrace.hpp b/src/hotspot/share/gc/shenandoah/shenandoahTrace.hpp index 116968103de..e5c80e0705f 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahTrace.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahTrace.hpp @@ -34,11 +34,14 @@ class ShenandoahTracer : public GCTracer, public CHeapObj { public: ShenandoahTracer() : GCTracer(Shenandoah) {} - // Sends a JFR event (if enabled) summarizing the composition of the collection set + // Sends a JFR event summarizing the composition of the collection set static void report_evacuation_info(const ShenandoahCollectionSet* cset, - size_t free_regions, size_t regions_promoted_humongous, size_t regions_promoted_regular, - size_t regular_promoted_garbage, size_t regular_promoted_free, size_t regions_immediate, - size_t immediate_size); + size_t free_regions, size_t regions_immediate, size_t immediate_size); + + // Sends a JFR event summarizing in-place promotion activity (generational mode only) + static void report_promotion_info(const ShenandoahCollectionSet* cset, + size_t regions_promoted_humongous, size_t humongous_promoted_garbage, size_t humongous_promoted_free, + size_t regions_promoted_regular, size_t regular_promoted_garbage, size_t regular_promoted_free); }; #endif diff --git a/src/hotspot/share/gc/shenandoah/shenandoahUtils.cpp b/src/hotspot/share/gc/shenandoah/shenandoahUtils.cpp index dea47fcbf4f..5af2e274833 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahUtils.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahUtils.cpp @@ -56,15 +56,14 @@ const char* ShenandoahGCSession::cycle_end_message(ShenandoahGenerationType type } } -ShenandoahGCSession::ShenandoahGCSession(GCCause::Cause cause, ShenandoahGeneration* generation) : +ShenandoahGCSession::ShenandoahGCSession(GCCause::Cause cause, ShenandoahGeneration* generation, + bool is_degenerated, bool is_out_of_cycle) : _heap(ShenandoahHeap::heap()), _generation(generation), _timer(_heap->gc_timer()), _tracer(_heap->tracer()) { assert(!ShenandoahGCPhase::is_current_phase_valid(), "No current GC phase"); - - _heap->on_cycle_start(cause, _generation); - + _heap->on_cycle_start(cause, _generation, is_degenerated, is_out_of_cycle); _timer->register_gc_start(); _tracer->report_gc_start(cause, _timer->gc_start()); _heap->trace_heap_before_gc(_tracer); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahUtils.hpp b/src/hotspot/share/gc/shenandoah/shenandoahUtils.hpp index 6ef4cd7c702..1ed6e43e3e1 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahUtils.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahUtils.hpp @@ -70,7 +70,8 @@ private: static const char* cycle_end_message(ShenandoahGenerationType type); public: - ShenandoahGCSession(GCCause::Cause cause, ShenandoahGeneration* generation); + ShenandoahGCSession(GCCause::Cause cause, ShenandoahGeneration* generation, + bool is_degenerated = false, bool is_out_of_cycle = false); ~ShenandoahGCSession(); }; @@ -187,7 +188,7 @@ public: type == VM_Operation::VMOp_ShenandoahFinalMarkStartEvac || type == VM_Operation::VMOp_ShenandoahInitUpdateRefs || type == VM_Operation::VMOp_ShenandoahFinalUpdateRefs || - type == VM_Operation::VMOp_ShenandoahFinalRoots || + type == VM_Operation::VMOp_ShenandoahFinalVerify || type == VM_Operation::VMOp_ShenandoahFullGC || type == VM_Operation::VMOp_ShenandoahDegeneratedGC; } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.cpp b/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.cpp index 6b45842f781..97dd7e5cda1 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.cpp @@ -135,12 +135,12 @@ void VM_ShenandoahFinalUpdateRefs::doit() { _gc->entry_final_update_refs(); } -VM_ShenandoahFinalRoots::VM_ShenandoahFinalRoots(ShenandoahConcurrentGC* gc) +VM_ShenandoahFinalVerify::VM_ShenandoahFinalVerify(ShenandoahConcurrentGC* gc) : VM_ShenandoahOperation(gc->generation()), _gc(gc) { } -void VM_ShenandoahFinalRoots::doit() { - ShenandoahGCPauseMark mark(_gc_id, "Final Roots", SvcGCMarker::CONCURRENT); +void VM_ShenandoahFinalVerify::doit() { + ShenandoahGCPauseMark mark(_gc_id, "Final Verify", SvcGCMarker::CONCURRENT); set_active_generation(); - _gc->entry_verify_final_roots(); + _gc->entry_final_verify(); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.hpp b/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.hpp index d565a3df22c..f8b99c71b14 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.hpp @@ -38,7 +38,7 @@ class ShenandoahFullGC; // - VM_ShenandoahFinalMarkStartEvac: finish up concurrent marking, and start evacuation // - VM_ShenandoahInitUpdateRefs: initiate update references // - VM_ShenandoahFinalUpdateRefs: finish up update references -// - VM_ShenandoahFinalRoots: finish up roots on a non-evacuating cycle +// - VM_ShenandoahFinalVerify: final verification at the end of the cycle // - VM_ShenandoahReferenceOperation: // - VM_ShenandoahFullGC: do full GC // - VM_ShenandoahDegeneratedGC: do STW degenerated GC @@ -127,12 +127,12 @@ public: void doit() override; }; -class VM_ShenandoahFinalRoots: public VM_ShenandoahOperation { +class VM_ShenandoahFinalVerify: public VM_ShenandoahOperation { ShenandoahConcurrentGC* const _gc; public: - explicit VM_ShenandoahFinalRoots(ShenandoahConcurrentGC* gc); - VM_Operation::VMOp_Type type() const override { return VMOp_ShenandoahFinalRoots; } - const char* name() const override { return "Shenandoah Final Roots"; } + explicit VM_ShenandoahFinalVerify(ShenandoahConcurrentGC* gc); + VM_Operation::VMOp_Type type() const override { return VMOp_ShenandoahFinalVerify; } + const char* name() const override { return "Shenandoah Final Verify"; } void doit() override; }; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp index afef11640c9..a801023fcc4 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp @@ -1073,7 +1073,7 @@ void ShenandoahVerifier::verify_at_safepoint(ShenandoahGeneration* generation, log_info(gc)("Verify %s, Level %zd (%zu reachable, %zu marked)", label, ShenandoahVerifyLevel, count_reachable, count_marked); - FREE_C_HEAP_ARRAY(ShenandoahLivenessData, ld); + FREE_C_HEAP_ARRAY(ld); } void ShenandoahVerifier::verify_generic(ShenandoahGeneration* generation, VerifyOption vo) { @@ -1180,7 +1180,7 @@ void ShenandoahVerifier::verify_before_update_refs(ShenandoahGeneration* generat ); } -// We have not yet cleanup (reclaimed) the collection set +// We have not yet cleaned up (reclaimed) the collection set void ShenandoahVerifier::verify_after_update_refs(ShenandoahGeneration* generation) { verify_at_safepoint( generation, @@ -1197,6 +1197,23 @@ void ShenandoahVerifier::verify_after_update_refs(ShenandoahGeneration* generati ); } +// We have not yet cleaned up (reclaimed) the collection set +void ShenandoahVerifier::verify_after_gc(ShenandoahGeneration* generation) { + verify_at_safepoint( + generation, + "After GC", + _verify_remembered_disable, // do not verify remembered set + _verify_forwarded_none, // no forwarded references + _verify_marked_complete, // bitmaps might be stale, but alloc-after-mark should be well + _verify_cset_none, // no cset references, all updated + _verify_liveness_disable, // no reliable liveness data anymore + _verify_regions_nocset, // no cset regions, trash regions have appeared + // expect generation and heap sizes to match exactly, including trash + _verify_size_exact_including_trash, + _verify_gcstate_stable // GC state was turned off + ); +} + void ShenandoahVerifier::verify_after_degenerated(ShenandoahGeneration* generation) { verify_at_safepoint( generation, diff --git a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.hpp b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.hpp index 7e683cf7af8..0479d5f67ce 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.hpp @@ -220,6 +220,7 @@ public: void verify_before_evacuation(ShenandoahGeneration* generation); void verify_before_update_refs(ShenandoahGeneration* generation); void verify_after_update_refs(ShenandoahGeneration* generation); + void verify_after_gc(ShenandoahGeneration* generation); void verify_before_fullgc(ShenandoahGeneration* generation); void verify_after_fullgc(ShenandoahGeneration* generation); void verify_after_degenerated(ShenandoahGeneration* generation); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.cpp index f00ce16136f..7a76bc50078 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.cpp @@ -131,6 +131,14 @@ size_t ShenandoahYoungGeneration::free_unaffiliated_regions() const { return _free_set->young_unaffiliated_regions(); } +size_t ShenandoahYoungGeneration::available_with_reserve() const { + shenandoah_assert_heaplocked(); + ShenandoahFreeSet* free_set = ShenandoahHeap::heap()->free_set(); + size_t mutator_available = free_set->available_locked(); + size_t collector_available = free_set->collector_available_locked(); + return mutator_available + collector_available; +} + size_t ShenandoahYoungGeneration::available() const { // The collector reserve may eat into what the mutator is allowed to use. Make sure we are looking // at what is available to the mutator when reporting how much memory is available. diff --git a/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.hpp b/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.hpp index 930c5ff1747..c3b6944ec80 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.hpp @@ -82,6 +82,8 @@ public: size_t get_affiliated_region_count() const override; size_t max_capacity() const override; + // Return sum of bytes available to mutator and to Collector, assuming heap lock is held. + size_t available_with_reserve() const; size_t available() const override; size_t soft_mutator_available() const override; diff --git a/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp b/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp index d3e9a1f9fae..2c5ba726ef2 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp @@ -152,9 +152,6 @@ "evvort even if the usage of old generation is below " \ "ShenandoahIgnoreOldGrowthBelowPercentage.") \ \ - product(bool, ShenandoahGenerationalAdaptiveTenuring, true, EXPERIMENTAL, \ - "(Generational mode only) Dynamically adapt tenuring age.") \ - \ product(bool, ShenandoahGenerationalCensusIgnoreOlderCohorts, true, \ EXPERIMENTAL,\ "(Generational mode only) Ignore mortality rates older than the " \ @@ -179,8 +176,7 @@ "(Generational mode only) Cohort mortality rates below this " \ "value will be treated as indicative of longevity, leading to " \ "tenuring. A lower value delays tenuring, a higher value hastens "\ - "it. Used only when ShenandoahGenerationalhenAdaptiveTenuring is "\ - "enabled.") \ + "it.") \ range(0.001,0.999) \ \ product(size_t, ShenandoahGenerationalTenuringCohortPopulationThreshold, \ @@ -564,9 +560,6 @@ product(bool, ShenandoahLoadRefBarrier, true, DIAGNOSTIC, \ "Turn on/off load-reference barriers in Shenandoah") \ \ - product(bool, ShenandoahStackWatermarkBarrier, true, DIAGNOSTIC, \ - "Turn on/off stack watermark barriers in Shenandoah") \ - \ develop(bool, ShenandoahVerifyOptoBarriers, trueInDebug, \ "Verify no missing barriers in C2.") \ \ diff --git a/src/hotspot/share/gc/z/c2/zBarrierSetC2.cpp b/src/hotspot/share/gc/z/c2/zBarrierSetC2.cpp index 650918e2d30..0a3dac1e100 100644 --- a/src/hotspot/share/gc/z/c2/zBarrierSetC2.cpp +++ b/src/hotspot/share/gc/z/c2/zBarrierSetC2.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -442,7 +442,7 @@ void ZBarrierSetC2::clone_at_expansion(PhaseMacroExpand* phase, ArrayCopyNode* a assert(src_offset == dest_offset, "should be equal"); const jlong offset = src_offset->get_long(); if (offset != arrayOopDesc::base_offset_in_bytes(T_OBJECT)) { - assert(!UseCompressedClassPointers || UseCompactObjectHeaders, "should only happen without compressed class pointers"); + assert(UseCompactObjectHeaders, "should only happen with COH"); assert((arrayOopDesc::base_offset_in_bytes(T_OBJECT) - offset) == BytesPerLong, "unexpected offset"); length = phase->transform_later(new SubLNode(length, phase->longcon(1))); // Size is in longs src_offset = phase->longcon(arrayOopDesc::base_offset_in_bytes(T_OBJECT)); diff --git a/src/hotspot/share/gc/z/zBarrierSetAssembler.hpp b/src/hotspot/share/gc/z/zBarrierSetAssembler.hpp index bcc757d6132..7b15813678a 100644 --- a/src/hotspot/share/gc/z/zBarrierSetAssembler.hpp +++ b/src/hotspot/share/gc/z/zBarrierSetAssembler.hpp @@ -34,6 +34,9 @@ public: static Address load_bad_mask_from_jni_env(Register env); static Address mark_bad_mask_from_jni_env(Register env); + + virtual void register_reloc_addresses(GrowableArray
&entries, int begin, int count) { } + virtual void retrieve_reloc_addresses(address start, address end, GrowableArray
&entries) { } }; // Needs to be included after definition of ZBarrierSetAssemblerBase diff --git a/src/hotspot/share/gc/z/zBarrierSetNMethod.cpp b/src/hotspot/share/gc/z/zBarrierSetNMethod.cpp index d80ce4e149d..a439b3a167b 100644 --- a/src/hotspot/share/gc/z/zBarrierSetNMethod.cpp +++ b/src/hotspot/share/gc/z/zBarrierSetNMethod.cpp @@ -33,6 +33,7 @@ #include "gc/z/zThreadLocalData.hpp" #include "gc/z/zUncoloredRoot.inline.hpp" #include "logging/log.hpp" +#include "runtime/icache.hpp" #include "runtime/threadWXSetters.inline.hpp" bool ZBarrierSetNMethod::nmethod_entry_barrier(nmethod* nm) { @@ -70,12 +71,15 @@ bool ZBarrierSetNMethod::nmethod_entry_barrier(nmethod* nm) { return false; } - // Heal barriers - ZNMethod::nmethod_patch_barriers(nm); + { + ICacheInvalidationContext icic; + // Heal barriers + ZNMethod::nmethod_patch_barriers(nm, &icic); - // Heal oops - ZUncoloredRootProcessWeakOopClosure cl(ZNMethod::color(nm)); - ZNMethod::nmethod_oops_do_inner(nm, &cl); + // Heal oops + ZUncoloredRootProcessWeakOopClosure cl(ZNMethod::color(nm)); + ZNMethod::nmethod_oops_do_inner(nm, &cl, &icic); + } const uintptr_t prev_color = ZNMethod::color(nm); const uintptr_t new_color = *ZPointerStoreGoodMaskLowOrderBitsAddr; diff --git a/src/hotspot/share/gc/z/zDebug.gdb b/src/hotspot/share/gc/z/zDebug.gdb index d502eea7ce3..df087c4a42d 100644 --- a/src/hotspot/share/gc/z/zDebug.gdb +++ b/src/hotspot/share/gc/z/zDebug.gdb @@ -50,11 +50,7 @@ define zpo end printf "\t Page: %llu\n", ((uintptr_t)$obj & ZAddressOffsetMask) >> ZGranuleSizeShift x/16gx $obj - if (UseCompressedClassPointers) - set $klass = (Klass*)(void*)((uintptr_t)CompressedKlassPointers::_base +((uintptr_t)$obj->_metadata->_compressed_klass << CompressedKlassPointers::_shift)) - else - set $klass = $obj->_metadata->_klass - end + set $klass = (Klass*)(void*)((uintptr_t)CompressedKlassPointers::_base +((uintptr_t)$obj->_compressed_klass << CompressedKlassPointers::_shift)) printf "Mark: 0x%016llx\tKlass: %s\n", (uintptr_t)$obj->_mark, (char*)$klass->_name->_body end diff --git a/src/hotspot/share/gc/z/zForwardingAllocator.cpp b/src/hotspot/share/gc/z/zForwardingAllocator.cpp index 451a1d62754..37b5b8f520c 100644 --- a/src/hotspot/share/gc/z/zForwardingAllocator.cpp +++ b/src/hotspot/share/gc/z/zForwardingAllocator.cpp @@ -30,11 +30,11 @@ ZForwardingAllocator::ZForwardingAllocator() _top(nullptr) {} ZForwardingAllocator::~ZForwardingAllocator() { - FREE_C_HEAP_ARRAY(char, _start); + FREE_C_HEAP_ARRAY(_start); } void ZForwardingAllocator::reset(size_t size) { - _start = REALLOC_C_HEAP_ARRAY(char, _start, size, mtGC); + _start = REALLOC_C_HEAP_ARRAY(_start, size, mtGC); _top.store_relaxed(_start); _end = _start + size; } diff --git a/src/hotspot/share/gc/z/zGeneration.cpp b/src/hotspot/share/gc/z/zGeneration.cpp index 27f352a624f..0f9f4e34a5e 100644 --- a/src/hotspot/share/gc/z/zGeneration.cpp +++ b/src/hotspot/share/gc/z/zGeneration.cpp @@ -58,6 +58,7 @@ #include "prims/jvmtiTagMap.hpp" #include "runtime/continuation.hpp" #include "runtime/handshake.hpp" +#include "runtime/icache.hpp" #include "runtime/safepoint.hpp" #include "runtime/threads.hpp" #include "runtime/vmOperations.hpp" @@ -1434,12 +1435,15 @@ public: virtual void do_nmethod(nmethod* nm) { ZLocker locker(ZNMethod::lock_for_nmethod(nm)); if (_bs_nm->is_armed(nm)) { - // Heal barriers - ZNMethod::nmethod_patch_barriers(nm); + { + ICacheInvalidationContext icic; + // Heal barriers + ZNMethod::nmethod_patch_barriers(nm, &icic); - // Heal oops - ZUncoloredRootProcessOopClosure cl(ZNMethod::color(nm)); - ZNMethod::nmethod_oops_do_inner(nm, &cl); + // Heal oops + ZUncoloredRootProcessOopClosure cl(ZNMethod::color(nm)); + ZNMethod::nmethod_oops_do_inner(nm, &cl, &icic); + } log_trace(gc, nmethod)("nmethod: " PTR_FORMAT " visited by old remapping", p2i(nm)); diff --git a/src/hotspot/share/gc/z/zMark.cpp b/src/hotspot/share/gc/z/zMark.cpp index 03701ae9998..ac7d86db240 100644 --- a/src/hotspot/share/gc/z/zMark.cpp +++ b/src/hotspot/share/gc/z/zMark.cpp @@ -59,6 +59,7 @@ #include "oops/oop.inline.hpp" #include "runtime/continuation.hpp" #include "runtime/handshake.hpp" +#include "runtime/icache.hpp" #include "runtime/javaThread.hpp" #include "runtime/prefetch.inline.hpp" #include "runtime/safepointMechanism.hpp" @@ -718,12 +719,15 @@ public: virtual void do_nmethod(nmethod* nm) { ZLocker locker(ZNMethod::lock_for_nmethod(nm)); if (_bs_nm->is_armed(nm)) { - // Heal barriers - ZNMethod::nmethod_patch_barriers(nm); + { + ICacheInvalidationContext icic; + // Heal barriers + ZNMethod::nmethod_patch_barriers(nm, &icic); - // Heal oops - ZUncoloredRootMarkOopClosure cl(ZNMethod::color(nm)); - ZNMethod::nmethod_oops_do_inner(nm, &cl); + // Heal oops + ZUncoloredRootMarkOopClosure cl(ZNMethod::color(nm)); + ZNMethod::nmethod_oops_do_inner(nm, &cl, &icic); + } // CodeCache unloading support nm->mark_as_maybe_on_stack(); @@ -753,10 +757,6 @@ public: if (_bs_nm->is_armed(nm)) { const uintptr_t prev_color = ZNMethod::color(nm); - // Heal oops - ZUncoloredRootMarkYoungOopClosure cl(prev_color); - ZNMethod::nmethod_oops_do_inner(nm, &cl); - // Disarm only the young marking, not any potential old marking cycle const uintptr_t old_marked_mask = ZPointerMarkedMask ^ (ZPointerMarkedYoung0 | ZPointerMarkedYoung1); @@ -767,9 +767,16 @@ public: // Check if disarming for young mark, completely disarms the nmethod entry barrier const bool complete_disarm = ZPointer::is_store_good(new_disarm_value_ptr); - if (complete_disarm) { - // We are about to completely disarm the nmethod, must take responsibility to patch all barriers before disarming - ZNMethod::nmethod_patch_barriers(nm); + { + ICacheInvalidationContext icic; + if (complete_disarm) { + // We are about to completely disarm the nmethod, must take responsibility to patch all barriers before disarming + ZNMethod::nmethod_patch_barriers(nm, &icic); + } + + // Heal oops + ZUncoloredRootMarkYoungOopClosure cl(prev_color); + ZNMethod::nmethod_oops_do_inner(nm, &cl, &icic); } _bs_nm->guard_with(nm, (int)untype(new_disarm_value_ptr)); diff --git a/src/hotspot/share/gc/z/zNMethod.cpp b/src/hotspot/share/gc/z/zNMethod.cpp index 780bc9e3bf7..a1348b63b6f 100644 --- a/src/hotspot/share/gc/z/zNMethod.cpp +++ b/src/hotspot/share/gc/z/zNMethod.cpp @@ -50,6 +50,7 @@ #include "oops/oop.inline.hpp" #include "runtime/atomicAccess.hpp" #include "runtime/continuation.hpp" +#include "runtime/icache.hpp" #include "utilities/debug.hpp" static ZNMethodData* gc_data(const nmethod* nm) { @@ -245,8 +246,16 @@ void ZNMethod::set_guard_value(nmethod* nm, int value) { } void ZNMethod::nmethod_patch_barriers(nmethod* nm) { + ICacheInvalidationContext icic; + nmethod_patch_barriers(nm, &icic); +} + +void ZNMethod::nmethod_patch_barriers(nmethod* nm, ICacheInvalidationContext* icic) { ZBarrierSetAssembler* const bs_asm = ZBarrierSet::assembler(); ZArrayIterator iter(gc_data(nm)->barriers()); + if (gc_data(nm)->barriers()->is_nonempty()) { + icic->set_has_modified_code(); + } for (ZNMethodDataBarrier barrier; iter.next(&barrier);) { bs_asm->patch_barrier_relocation(barrier._reloc_addr, barrier._reloc_format); } @@ -258,6 +267,11 @@ void ZNMethod::nmethod_oops_do(nmethod* nm, OopClosure* cl) { } void ZNMethod::nmethod_oops_do_inner(nmethod* nm, OopClosure* cl) { + ICacheInvalidationContext icic; + nmethod_oops_do_inner(nm, cl, &icic); +} + +void ZNMethod::nmethod_oops_do_inner(nmethod* nm, OopClosure* cl, ICacheInvalidationContext* icic) { // Process oops table { oop* const begin = nm->oops_begin(); @@ -283,7 +297,7 @@ void ZNMethod::nmethod_oops_do_inner(nmethod* nm, OopClosure* cl) { // Process non-immediate oops if (data->has_non_immediate_oops()) { - nm->fix_oop_relocations(); + nm->fix_oop_relocations(icic); } } diff --git a/src/hotspot/share/gc/z/zNMethod.hpp b/src/hotspot/share/gc/z/zNMethod.hpp index 865ea11e7b9..2779151c576 100644 --- a/src/hotspot/share/gc/z/zNMethod.hpp +++ b/src/hotspot/share/gc/z/zNMethod.hpp @@ -56,9 +56,11 @@ public: static void set_guard_value(nmethod* nm, int value); static void nmethod_patch_barriers(nmethod* nm); + static void nmethod_patch_barriers(nmethod* nm, ICacheInvalidationContext* icic); static void nmethod_oops_do(nmethod* nm, OopClosure* cl); static void nmethod_oops_do_inner(nmethod* nm, OopClosure* cl); + static void nmethod_oops_do_inner(nmethod* nm, OopClosure* cl, ICacheInvalidationContext* icic); static void nmethods_do_begin(bool secondary); static void nmethods_do_end(bool secondary); diff --git a/src/hotspot/share/interpreter/bytecodeTracer.cpp b/src/hotspot/share/interpreter/bytecodeTracer.cpp index 2610a8a2bd6..4578a3eec4e 100644 --- a/src/hotspot/share/interpreter/bytecodeTracer.cpp +++ b/src/hotspot/share/interpreter/bytecodeTracer.cpp @@ -52,9 +52,9 @@ class BytecodePrinter { Bytecodes::Code _code; address _next_pc; // current decoding position int _flags; - bool _is_linked; + bool _use_cp_cache; - bool is_linked() const { return _is_linked; } + bool use_cp_cache() const { return _use_cp_cache; } void align() { _next_pc = align_up(_next_pc, sizeof(jint)); } int get_byte() { return *(jbyte*) _next_pc++; } // signed int get_index_u1() { return *(address)_next_pc++; } // returns 0x00 - 0xff as an int @@ -69,7 +69,7 @@ class BytecodePrinter { bool is_wide() const { return _is_wide; } Bytecodes::Code raw_code() const { return Bytecodes::Code(_code); } ConstantPool* constants() const { return method()->constants(); } - ConstantPoolCache* cpcache() const { assert(is_linked(), "must be"); return constants()->cache(); } + ConstantPoolCache* cpcache() const { assert(use_cp_cache(), "must be"); return constants()->cache(); } void print_constant(int i, outputStream* st); void print_cpcache_entry(int cpc_index, outputStream* st); @@ -94,8 +94,9 @@ class BytecodePrinter { ResourceMark rm; bool method_changed = _current_method != method(); _current_method = method(); - _is_linked = method->method_holder()->is_linked(); - assert(_is_linked, "this function must be called on methods that are already executing"); + _use_cp_cache = method->constants()->cache() != nullptr; + assert(method->method_holder()->is_linked(), + "this function must be called on methods that are already executing"); if (method_changed) { // Note 1: This code will not work as expected with true MT/MP. @@ -150,7 +151,8 @@ class BytecodePrinter { // BytecodeStream, which will skip wide bytecodes. void trace(const methodHandle& method, address bcp, outputStream* st) { _current_method = method(); - _is_linked = method->method_holder()->is_linked(); + // This may be called during linking after bytecodes are rewritten to point to the cpCache. + _use_cp_cache = method->constants()->cache() != nullptr; ResourceMark rm; Bytecodes::Code code = Bytecodes::code_at(method(), bcp); // Set is_wide @@ -301,7 +303,7 @@ void BytecodePrinter::print_invokedynamic(int indy_index, int cp_index, outputSt if (ClassPrinter::has_mode(_flags, ClassPrinter::PRINT_DYNAMIC)) { print_bsm(cp_index, st); - if (is_linked()) { + if (use_cp_cache()) { ResolvedIndyEntry* indy_entry = constants()->resolved_indy_entry_at(indy_index); st->print(" ResolvedIndyEntry: "); indy_entry->print_on(st); @@ -365,7 +367,7 @@ void BytecodePrinter::print_attributes(int bci, outputStream* st) { { int cp_index; if (Bytecodes::uses_cp_cache(raw_code())) { - assert(is_linked(), "fast ldc bytecode must be in linked classes"); + assert(use_cp_cache(), "fast ldc bytecode must be in linked classes"); int obj_index = get_index_u1(); cp_index = constants()->object_to_cp_index(obj_index); } else { @@ -380,7 +382,7 @@ void BytecodePrinter::print_attributes(int bci, outputStream* st) { { int cp_index; if (Bytecodes::uses_cp_cache(raw_code())) { - assert(is_linked(), "fast ldc bytecode must be in linked classes"); + assert(use_cp_cache(), "fast ldc bytecode must be in linked classes"); int obj_index = get_native_index_u2(); cp_index = constants()->object_to_cp_index(obj_index); } else { @@ -510,7 +512,7 @@ void BytecodePrinter::print_attributes(int bci, outputStream* st) { case Bytecodes::_getfield: { int cp_index; - if (is_linked()) { + if (use_cp_cache()) { int field_index = get_native_index_u2(); cp_index = cpcache()->resolved_field_entry_at(field_index)->constant_pool_index(); } else { @@ -525,7 +527,7 @@ void BytecodePrinter::print_attributes(int bci, outputStream* st) { case Bytecodes::_invokestatic: { int cp_index; - if (is_linked()) { + if (use_cp_cache()) { int method_index = get_native_index_u2(); ResolvedMethodEntry* method_entry = cpcache()->resolved_method_entry_at(method_index); cp_index = method_entry->constant_pool_index(); @@ -533,7 +535,7 @@ void BytecodePrinter::print_attributes(int bci, outputStream* st) { if (raw_code() == Bytecodes::_invokehandle && ClassPrinter::has_mode(_flags, ClassPrinter::PRINT_METHOD_HANDLE)) { - assert(is_linked(), "invokehandle is only in rewritten methods"); + assert(use_cp_cache(), "invokehandle is only in rewritten methods"); method_entry->print_on(st); if (method_entry->has_appendix()) { st->print(" appendix: "); @@ -550,7 +552,7 @@ void BytecodePrinter::print_attributes(int bci, outputStream* st) { case Bytecodes::_invokeinterface: { int cp_index; - if (is_linked()) { + if (use_cp_cache()) { int method_index = get_native_index_u2(); cp_index = cpcache()->resolved_method_entry_at(method_index)->constant_pool_index(); } else { @@ -566,7 +568,7 @@ void BytecodePrinter::print_attributes(int bci, outputStream* st) { { int indy_index; int cp_index; - if (is_linked()) { + if (use_cp_cache()) { indy_index = get_native_index_u4(); cp_index = constants()->resolved_indy_entry_at(indy_index)->constant_pool_index(); } else { diff --git a/src/hotspot/share/interpreter/bytecodes.cpp b/src/hotspot/share/interpreter/bytecodes.cpp index 1526b3c330e..a7914b6b93a 100644 --- a/src/hotspot/share/interpreter/bytecodes.cpp +++ b/src/hotspot/share/interpreter/bytecodes.cpp @@ -402,7 +402,7 @@ int Bytecodes::special_length_at(Bytecodes::Code code, address bcp, address end) case _fast_binaryswitch: // fall through case _fast_linearswitch: { address aligned_bcp = align_up(bcp + 1, jintSize); - if (end != nullptr && aligned_bcp + 2*jintSize >= end) { + if (end != nullptr && aligned_bcp + 2*jintSize > end) { return -1; // don't read past end of code buffer } // Promote calculation to 64 bits to do range checks, used by the verifier. diff --git a/src/hotspot/share/interpreter/interpreterRuntime.cpp b/src/hotspot/share/interpreter/interpreterRuntime.cpp index 59d5f29023c..cd0a062ebc8 100644 --- a/src/hotspot/share/interpreter/interpreterRuntime.cpp +++ b/src/hotspot/share/interpreter/interpreterRuntime.cpp @@ -36,6 +36,7 @@ #include "interpreter/interpreter.hpp" #include "interpreter/interpreterRuntime.hpp" #include "interpreter/linkResolver.hpp" +#include "interpreter/oopMapCache.hpp" #include "interpreter/templateTable.hpp" #include "jvm_io.h" #include "logging/log.hpp" @@ -243,9 +244,9 @@ JRT_ENTRY(void, InterpreterRuntime::multianewarray(JavaThread* current, jint* fi // We may want to pass in more arguments - could make this slightly faster LastFrameAccessor last_frame(current); ConstantPool* constants = last_frame.method()->constants(); - int i = last_frame.get_index_u2(Bytecodes::_multianewarray); - Klass* klass = constants->klass_at(i, CHECK); - int nof_dims = last_frame.number_of_dimensions(); + int i = last_frame.get_index_u2(Bytecodes::_multianewarray); + Klass* klass = constants->klass_at(i, CHECK); + int nof_dims = last_frame.number_of_dimensions(); assert(klass->is_klass(), "not a class"); assert(nof_dims >= 1, "multianewarray rank must be nonzero"); @@ -756,12 +757,10 @@ JRT_LEAF(void, InterpreterRuntime::monitorexit(BasicObjectLock* elem)) elem->set_obj(nullptr); JRT_END - JRT_ENTRY(void, InterpreterRuntime::throw_illegal_monitor_state_exception(JavaThread* current)) THROW(vmSymbols::java_lang_IllegalMonitorStateException()); JRT_END - JRT_ENTRY(void, InterpreterRuntime::new_illegal_monitor_state_exception(JavaThread* current)) // Returns an illegal exception to install into the current thread. The // pending_exception flag is cleared so normal exception handling does not @@ -1529,4 +1528,17 @@ bool InterpreterRuntime::is_preemptable_call(address entry_point) { entry_point == CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_from_cache) || entry_point == CAST_FROM_FN_PTR(address, InterpreterRuntime::_new); } + +void InterpreterRuntime::generate_oop_map_alot() { + JavaThread* current = JavaThread::current(); + LastFrameAccessor last_frame(current); + if (last_frame.is_interpreted_frame()) { + ResourceMark rm(current); + InterpreterOopMap mask; + methodHandle mh(current, last_frame.method()); + int bci = last_frame.bci(); + log_info(generateoopmap)("Generating oopmap for method %s at bci %d", mh->name_and_sig_as_C_string(), bci); + OopMapCache::compute_one_oop_map(mh, bci, &mask); + } +} #endif // ASSERT diff --git a/src/hotspot/share/interpreter/interpreterRuntime.hpp b/src/hotspot/share/interpreter/interpreterRuntime.hpp index 70ceeb0b2af..3228027fa93 100644 --- a/src/hotspot/share/interpreter/interpreterRuntime.hpp +++ b/src/hotspot/share/interpreter/interpreterRuntime.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -173,6 +173,8 @@ private: // Virtual Thread Preemption DEBUG_ONLY(static bool is_preemptable_call(address entry_point);) + + DEBUG_ONLY(static void generate_oop_map_alot();) }; diff --git a/src/hotspot/share/interpreter/linkResolver.cpp b/src/hotspot/share/interpreter/linkResolver.cpp index c82398b654c..25fff580c9d 100644 --- a/src/hotspot/share/interpreter/linkResolver.cpp +++ b/src/hotspot/share/interpreter/linkResolver.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -1045,7 +1045,7 @@ void LinkResolver::resolve_field(fieldDescriptor& fd, stringStream ss; ss.print("Update to %s final field %s.%s attempted from a different class (%s) than the field's declaring class", is_static ? "static" : "non-static", resolved_klass->external_name(), fd.name()->as_C_string(), - current_klass->external_name()); + current_klass->external_name()); THROW_MSG(vmSymbols::java_lang_IllegalAccessError(), ss.as_string()); } @@ -1260,7 +1260,7 @@ void LinkResolver::runtime_resolve_special_method(CallInfo& result, methodHandle sel_method(THREAD, resolved_method()); if (link_info.check_access() && - // check if the method is not + // check if the method is not , which is never inherited resolved_method->name() != vmSymbols::object_initializer_name()) { Klass* current_klass = link_info.current_klass(); @@ -1724,8 +1724,8 @@ void LinkResolver::resolve_invoke(CallInfo& result, Handle recv, const constantP } void LinkResolver::resolve_invoke(CallInfo& result, Handle& recv, - const methodHandle& attached_method, - Bytecodes::Code byte, TRAPS) { + const methodHandle& attached_method, + Bytecodes::Code byte, TRAPS) { Klass* defc = attached_method->method_holder(); Symbol* name = attached_method->name(); Symbol* type = attached_method->signature(); diff --git a/src/hotspot/share/interpreter/oopMapCache.cpp b/src/hotspot/share/interpreter/oopMapCache.cpp index d7c02296f33..34e226b00bf 100644 --- a/src/hotspot/share/interpreter/oopMapCache.cpp +++ b/src/hotspot/share/interpreter/oopMapCache.cpp @@ -92,11 +92,8 @@ class OopMapForCacheEntry: public GenerateOopMap { }; -OopMapForCacheEntry::OopMapForCacheEntry(const methodHandle& method, int bci, OopMapCacheEntry* entry) : GenerateOopMap(method) { - _bci = bci; - _entry = entry; - _stack_top = -1; -} +OopMapForCacheEntry::OopMapForCacheEntry(const methodHandle& method, int bci, OopMapCacheEntry* entry) : + GenerateOopMap(method, /*all_exception_edges*/ true), _entry(entry), _bci(bci), _stack_top(-1) { } bool OopMapForCacheEntry::compute_map(Thread* current) { @@ -107,6 +104,11 @@ bool OopMapForCacheEntry::compute_map(Thread* current) { } else { ResourceMark rm; if (!GenerateOopMap::compute_map(current)) { + // If compute_map fails, print the exception message, which is generated if + // this is a JavaThread, otherwise compute_map calls fatal so we don't get here. + if (exception() != nullptr) { + exception()->print(); + } fatal("Unrecoverable verification or out-of-memory error"); return false; } @@ -154,7 +156,7 @@ InterpreterOopMap::InterpreterOopMap() { InterpreterOopMap::~InterpreterOopMap() { if (has_valid_mask() && mask_size() > small_mask_limit) { assert(_bit_mask[0] != 0, "should have pointer to C heap"); - FREE_C_HEAP_ARRAY(uintptr_t, _bit_mask[0]); + FREE_C_HEAP_ARRAY((uintptr_t*)_bit_mask[0]); } } @@ -256,8 +258,8 @@ bool OopMapCacheEntry::verify_mask(CellTypeState* vars, CellTypeState* stack, in if (log) st.print("Locals (%d): ", max_locals); for(int i = 0; i < max_locals; i++) { - bool v1 = is_oop(i) ? true : false; - bool v2 = vars[i].is_reference() ? true : false; + bool v1 = is_oop(i); + bool v2 = vars[i].is_reference(); assert(v1 == v2, "locals oop mask generation error"); if (log) st.print("%d", v1 ? 1 : 0); } @@ -265,8 +267,8 @@ bool OopMapCacheEntry::verify_mask(CellTypeState* vars, CellTypeState* stack, in if (log) st.print("Stack (%d): ", stack_top); for(int j = 0; j < stack_top; j++) { - bool v1 = is_oop(max_locals + j) ? true : false; - bool v2 = stack[j].is_reference() ? true : false; + bool v1 = is_oop(max_locals + j); + bool v2 = stack[j].is_reference(); assert(v1 == v2, "stack oop mask generation error"); if (log) st.print("%d", v1 ? 1 : 0); } @@ -286,7 +288,7 @@ void OopMapCacheEntry::deallocate_bit_mask() { if (mask_size() > small_mask_limit && _bit_mask[0] != 0) { assert(!Thread::current()->resource_area()->contains((void*)_bit_mask[0]), "This bit mask should not be in the resource area"); - FREE_C_HEAP_ARRAY(uintptr_t, _bit_mask[0]); + FREE_C_HEAP_ARRAY((uintptr_t*)_bit_mask[0]); DEBUG_ONLY(_bit_mask[0] = 0;) } } @@ -315,6 +317,9 @@ void OopMapCacheEntry::fill(const methodHandle& method, int bci) { } else { OopMapForCacheEntry gen(method, bci, this); if (!gen.compute_map(Thread::current())) { + if (gen.exception() != nullptr) { + gen.exception()->print(); + } fatal("Unrecoverable verification or out-of-memory error"); } } @@ -350,7 +355,7 @@ void OopMapCacheEntry::set_mask(CellTypeState *vars, CellTypeState *stack, int s } // set oop bit - if ( cell->is_reference()) { + if (cell->is_reference()) { value |= (mask << oop_bit_number ); _num_oops++; } diff --git a/src/hotspot/share/jfr/jni/jfrJavaSupport.cpp b/src/hotspot/share/jfr/jni/jfrJavaSupport.cpp index a6e97ab227a..32710595d27 100644 --- a/src/hotspot/share/jfr/jni/jfrJavaSupport.cpp +++ b/src/hotspot/share/jfr/jni/jfrJavaSupport.cpp @@ -528,7 +528,7 @@ const char* JfrJavaSupport::c_str(jstring string, Thread* thread, bool c_heap /* void JfrJavaSupport::free_c_str(const char* str, bool c_heap) { if (c_heap) { - FREE_C_HEAP_ARRAY(char, str); + FREE_C_HEAP_ARRAY(str); } } diff --git a/src/hotspot/share/jfr/jni/jfrJniMethod.cpp b/src/hotspot/share/jfr/jni/jfrJniMethod.cpp index 885484020bd..0183bf634f6 100644 --- a/src/hotspot/share/jfr/jni/jfrJniMethod.cpp +++ b/src/hotspot/share/jfr/jni/jfrJniMethod.cpp @@ -410,6 +410,15 @@ JVM_ENTRY_NO_ENV(jlong, jfr_host_total_swap_memory(JNIEnv* env, jclass jvm)) return static_cast(total_swap_space); JVM_END +JVM_ENTRY_NO_ENV(jlong, jfr_host_memory_usage(JNIEnv* env, jclass jvm)) + physical_memory_size_type memory_usage = 0; + if (!os::Machine::used_memory(memory_usage)) { + // Return -1 to signal failure to get memory usage. + return static_cast(-1); + } + return static_cast(memory_usage); +JVM_END + JVM_ENTRY_NO_ENV(void, jfr_emit_data_loss(JNIEnv* env, jclass jvm, jlong bytes)) EventDataLoss::commit(bytes, min_jlong); JVM_END diff --git a/src/hotspot/share/jfr/jni/jfrJniMethod.hpp b/src/hotspot/share/jfr/jni/jfrJniMethod.hpp index 9769df57bd3..bcdaf7a99b7 100644 --- a/src/hotspot/share/jfr/jni/jfrJniMethod.hpp +++ b/src/hotspot/share/jfr/jni/jfrJniMethod.hpp @@ -163,6 +163,8 @@ jlong JNICALL jfr_host_total_memory(JNIEnv* env, jclass jvm); jlong JNICALL jfr_host_total_swap_memory(JNIEnv* env, jclass jvm); +jlong JNICALL jfr_host_memory_usage(JNIEnv* env, jclass jvm); + void JNICALL jfr_emit_data_loss(JNIEnv* env, jclass jvm, jlong bytes); jlong JNICALL jfr_register_stack_filter(JNIEnv* env, jclass jvm, jobjectArray classes, jobjectArray methods); diff --git a/src/hotspot/share/jfr/jni/jfrJniMethodRegistration.cpp b/src/hotspot/share/jfr/jni/jfrJniMethodRegistration.cpp index 2979f5c5c2d..0813289e840 100644 --- a/src/hotspot/share/jfr/jni/jfrJniMethodRegistration.cpp +++ b/src/hotspot/share/jfr/jni/jfrJniMethodRegistration.cpp @@ -101,6 +101,7 @@ JfrJniMethodRegistration::JfrJniMethodRegistration(JNIEnv* env) { (char*)"isContainerized", (char*)"()Z", (void*) jfr_is_containerized, (char*)"hostTotalMemory", (char*)"()J", (void*) jfr_host_total_memory, (char*)"hostTotalSwapMemory", (char*)"()J", (void*) jfr_host_total_swap_memory, + (char*)"hostMemoryUsage", (char*)"()J", (void*) jfr_host_memory_usage, (char*)"emitDataLoss", (char*)"(J)V", (void*)jfr_emit_data_loss, (char*)"registerStackFilter", (char*)"([Ljava/lang/String;[Ljava/lang/String;)J", (void*)jfr_register_stack_filter, (char*)"unregisterStackFilter", (char*)"(J)V", (void*)jfr_unregister_stack_filter, diff --git a/src/hotspot/share/jfr/leakprofiler/sampling/samplePriorityQueue.cpp b/src/hotspot/share/jfr/leakprofiler/sampling/samplePriorityQueue.cpp index 644cd25ec8a..5eac068467f 100644 --- a/src/hotspot/share/jfr/leakprofiler/sampling/samplePriorityQueue.cpp +++ b/src/hotspot/share/jfr/leakprofiler/sampling/samplePriorityQueue.cpp @@ -36,7 +36,7 @@ SamplePriorityQueue::SamplePriorityQueue(size_t size) : } SamplePriorityQueue::~SamplePriorityQueue() { - FREE_C_HEAP_ARRAY(ObjectSample*, _items); + FREE_C_HEAP_ARRAY(_items); _items = nullptr; } diff --git a/src/hotspot/share/jfr/metadata/metadata.xml b/src/hotspot/share/jfr/metadata/metadata.xml index 2b082165005..09d9e0ccabf 100644 --- a/src/hotspot/share/jfr/metadata/metadata.xml +++ b/src/hotspot/share/jfr/metadata/metadata.xml @@ -1273,16 +1273,22 @@ + + + + + + + + + - - - diff --git a/src/hotspot/share/jfr/periodic/jfrNetworkUtilization.cpp b/src/hotspot/share/jfr/periodic/jfrNetworkUtilization.cpp index 11e211f6505..1650ad7d9c0 100644 --- a/src/hotspot/share/jfr/periodic/jfrNetworkUtilization.cpp +++ b/src/hotspot/share/jfr/periodic/jfrNetworkUtilization.cpp @@ -46,7 +46,7 @@ static GrowableArray* _interfaces = nullptr; void JfrNetworkUtilization::destroy() { if (_interfaces != nullptr) { for (int i = 0; i < _interfaces->length(); ++i) { - FREE_C_HEAP_ARRAY(char, _interfaces->at(i).name); + FREE_C_HEAP_ARRAY(_interfaces->at(i).name); } delete _interfaces; _interfaces = nullptr; diff --git a/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp b/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp index 426ba4e7650..969c9ca60c1 100644 --- a/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp +++ b/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, 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 @@ -281,7 +281,9 @@ TRACE_REQUEST_FUNC(SystemProcess) { #if INCLUDE_JVMTI template -static void send_agent_event(AgentEvent& event, const JvmtiAgent* agent) { +static void send_agent_event(AgentEvent& event, const JvmtiAgent* agent, Ticks& timestamp) { + event.set_starttime(timestamp); + event.set_endtime(timestamp); event.set_name(agent->name()); event.set_options(agent->options()); event.set_dynamic(agent->is_dynamic()); @@ -292,29 +294,31 @@ static void send_agent_event(AgentEvent& event, const JvmtiAgent* agent) { TRACE_REQUEST_FUNC(JavaAgent) { JvmtiAgentList::Iterator it = JvmtiAgentList::java_agents(); + Ticks ticks = timestamp(); while (it.has_next()) { const JvmtiAgent* agent = it.next(); assert(agent->is_jplis(), "invariant"); EventJavaAgent event; - send_agent_event(event, agent); + send_agent_event(event, agent, ticks); } } -static void send_native_agent_events(JvmtiAgentList::Iterator& it) { +static void send_native_agent_events(JvmtiAgentList::Iterator& it, Ticks& timestamp) { while (it.has_next()) { const JvmtiAgent* agent = it.next(); assert(!agent->is_jplis(), "invariant"); EventNativeAgent event; event.set_path(agent->os_lib_path()); - send_agent_event(event, agent); + send_agent_event(event, agent, timestamp); } } TRACE_REQUEST_FUNC(NativeAgent) { + Ticks ticks = timestamp(); JvmtiAgentList::Iterator native_agents_it = JvmtiAgentList::native_agents(); - send_native_agent_events(native_agents_it); + send_native_agent_events(native_agents_it, ticks); JvmtiAgentList::Iterator xrun_agents_it = JvmtiAgentList::xrun_agents(); - send_native_agent_events(xrun_agents_it); + send_native_agent_events(xrun_agents_it, ticks); } #else // INCLUDE_JVMTI TRACE_REQUEST_FUNC(JavaAgent) {} diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdKlassQueue.cpp b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdKlassQueue.cpp index 9c57374d6c6..eab7de20545 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdKlassQueue.cpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdKlassQueue.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -75,7 +75,7 @@ static size_t element_size(bool compressed) { } static bool can_compress_element(traceid id) { - return Metaspace::using_class_space() && id < uncompressed_threshold; + return INCLUDE_CLASS_SPACE == 1 && id < uncompressed_threshold; } static size_t element_size(const Klass* klass) { diff --git a/src/hotspot/share/jfr/recorder/service/jfrOptionSet.cpp b/src/hotspot/share/jfr/recorder/service/jfrOptionSet.cpp index b212ee4ccba..ec8ab36d75a 100644 --- a/src/hotspot/share/jfr/recorder/service/jfrOptionSet.cpp +++ b/src/hotspot/share/jfr/recorder/service/jfrOptionSet.cpp @@ -799,7 +799,7 @@ void JfrOptionSet::release_start_flight_recording_options() { if (start_flight_recording_options_array != nullptr) { const int length = start_flight_recording_options_array->length(); for (int i = 0; i < length; ++i) { - FREE_C_HEAP_ARRAY(char, start_flight_recording_options_array->at(i)); + FREE_C_HEAP_ARRAY(start_flight_recording_options_array->at(i)); } delete start_flight_recording_options_array; start_flight_recording_options_array = nullptr; diff --git a/src/hotspot/share/jfr/recorder/stacktrace/jfrStackFilter.cpp b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackFilter.cpp index 1eb057b564e..39a1c621888 100644 --- a/src/hotspot/share/jfr/recorder/stacktrace/jfrStackFilter.cpp +++ b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackFilter.cpp @@ -55,6 +55,6 @@ JfrStackFilter::~JfrStackFilter() { Symbol::maybe_decrement_refcount(_method_names[i]); Symbol::maybe_decrement_refcount(_class_names[i]); } - FREE_C_HEAP_ARRAY(Symbol*, _method_names); - FREE_C_HEAP_ARRAY(Symbol*, _class_names); + FREE_C_HEAP_ARRAY(_method_names); + FREE_C_HEAP_ARRAY(_class_names); } diff --git a/src/hotspot/share/jfr/recorder/stacktrace/jfrStackFilterRegistry.cpp b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackFilterRegistry.cpp index 0721604b1c1..cb9811e7cae 100644 --- a/src/hotspot/share/jfr/recorder/stacktrace/jfrStackFilterRegistry.cpp +++ b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackFilterRegistry.cpp @@ -43,8 +43,8 @@ int64_t JfrStackFilterRegistry::add(jobjectArray classes, jobjectArray methods, Symbol** method_names = JfrJavaSupport::symbol_array(methods, jt, &m_size, true); assert(method_names != nullptr, "invariant"); if (c_size != m_size) { - FREE_C_HEAP_ARRAY(Symbol*, class_names); - FREE_C_HEAP_ARRAY(Symbol*, method_names); + FREE_C_HEAP_ARRAY(class_names); + FREE_C_HEAP_ARRAY(method_names); JfrJavaSupport::throw_internal_error("Method array size doesn't match class array size", jt); return STACK_FILTER_ERROR_CODE; } diff --git a/src/hotspot/share/jfr/support/methodtracer/jfrFilter.cpp b/src/hotspot/share/jfr/support/methodtracer/jfrFilter.cpp index 2a977f9e823..767036b57f2 100644 --- a/src/hotspot/share/jfr/support/methodtracer/jfrFilter.cpp +++ b/src/hotspot/share/jfr/support/methodtracer/jfrFilter.cpp @@ -51,10 +51,10 @@ JfrFilter::~JfrFilter() { Symbol::maybe_decrement_refcount(_method_names[i]); Symbol::maybe_decrement_refcount(_annotation_names[i]); } - FREE_C_HEAP_ARRAY(Symbol*, _class_names); - FREE_C_HEAP_ARRAY(Symbol*, _method_names); - FREE_C_HEAP_ARRAY(Symbol*, _annotation_names); - FREE_C_HEAP_ARRAY(int, _modifications); + FREE_C_HEAP_ARRAY(_class_names); + FREE_C_HEAP_ARRAY(_method_names); + FREE_C_HEAP_ARRAY(_annotation_names); + FREE_C_HEAP_ARRAY(_modifications); } bool JfrFilter::can_instrument_module(const ModuleEntry* module) const { diff --git a/src/hotspot/share/jfr/support/methodtracer/jfrFilterManager.cpp b/src/hotspot/share/jfr/support/methodtracer/jfrFilterManager.cpp index d9081efa08c..ea031f9f205 100644 --- a/src/hotspot/share/jfr/support/methodtracer/jfrFilterManager.cpp +++ b/src/hotspot/share/jfr/support/methodtracer/jfrFilterManager.cpp @@ -130,10 +130,10 @@ bool JfrFilterManager::install(jobjectArray classes, jobjectArray methods, jobje modifications[i] = modification_tah->int_at(i); } if (class_size != method_size || class_size != annotation_size || class_size != modification_size) { - FREE_C_HEAP_ARRAY(Symbol*, class_names); - FREE_C_HEAP_ARRAY(Symbol*, method_names); - FREE_C_HEAP_ARRAY(Symbol*, annotation_names); - FREE_C_HEAP_ARRAY(int, modifications); + FREE_C_HEAP_ARRAY(class_names); + FREE_C_HEAP_ARRAY(method_names); + FREE_C_HEAP_ARRAY(annotation_names); + FREE_C_HEAP_ARRAY(modifications); JfrJavaSupport::throw_internal_error("Method array sizes don't match", jt); return false; } diff --git a/src/hotspot/share/jfr/utilities/jfrConcurrentHashtable.inline.hpp b/src/hotspot/share/jfr/utilities/jfrConcurrentHashtable.inline.hpp index 6d6908234a3..22028a96e55 100644 --- a/src/hotspot/share/jfr/utilities/jfrConcurrentHashtable.inline.hpp +++ b/src/hotspot/share/jfr/utilities/jfrConcurrentHashtable.inline.hpp @@ -59,7 +59,7 @@ inline JfrConcurrentHashtable::JfrConcurrentHashtable(uns template class TableEntry> inline JfrConcurrentHashtable::~JfrConcurrentHashtable() { - FREE_C_HEAP_ARRAY(Bucket, _buckets); + FREE_C_HEAP_ARRAY(_buckets); } template class TableEntry> diff --git a/src/hotspot/share/jfr/utilities/jfrHashtable.hpp b/src/hotspot/share/jfr/utilities/jfrHashtable.hpp index 0be2d92ed19..ce4c920cf8b 100644 --- a/src/hotspot/share/jfr/utilities/jfrHashtable.hpp +++ b/src/hotspot/share/jfr/utilities/jfrHashtable.hpp @@ -93,7 +93,7 @@ class JfrBasicHashtable : public CHeapObj { --_number_of_entries; } void free_buckets() { - FREE_C_HEAP_ARRAY(Bucket, _buckets); + FREE_C_HEAP_ARRAY(_buckets); } TableEntry* bucket(size_t i) { return _buckets[i].get_entry();} TableEntry** bucket_addr(size_t i) { return _buckets[i].entry_addr(); } diff --git a/src/hotspot/share/jfr/utilities/jfrSet.hpp b/src/hotspot/share/jfr/utilities/jfrSet.hpp index c443434800a..3e828d51ec3 100644 --- a/src/hotspot/share/jfr/utilities/jfrSet.hpp +++ b/src/hotspot/share/jfr/utilities/jfrSet.hpp @@ -82,7 +82,7 @@ class JfrSetStorage : public AnyObj { ~JfrSetStorage() { if (CONFIG::alloc_type() == C_HEAP) { - FREE_C_HEAP_ARRAY(K, _table); + FREE_C_HEAP_ARRAY(_table); } } @@ -160,7 +160,7 @@ class JfrSet : public JfrSetStorage { } } if (CONFIG::alloc_type() == AnyObj::C_HEAP) { - FREE_C_HEAP_ARRAY(K, old_table); + FREE_C_HEAP_ARRAY(old_table); } assert(_table_mask + 1 == this->_table_size, "invariant"); assert(_resize_threshold << 1 == this->_table_size, "invariant"); diff --git a/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp b/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp index 6214f6a2746..6048c19d911 100644 --- a/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp +++ b/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp @@ -206,13 +206,8 @@ void CompilerToVM::Data::initialize(JVMCI_TRAPS) { Universe_narrow_oop_base = nullptr; Universe_narrow_oop_shift = 0; } - if (UseCompressedClassPointers) { - Universe_narrow_klass_base = CompressedKlassPointers::base(); - Universe_narrow_klass_shift = CompressedKlassPointers::shift(); - } else { - Universe_narrow_klass_base = nullptr; - Universe_narrow_klass_shift = 0; - } + Universe_narrow_klass_base = CompressedKlassPointers::base(); + Universe_narrow_klass_shift = CompressedKlassPointers::shift(); Universe_non_oop_bits = Universe::non_oop_word(); Universe_verify_oop_mask = Universe::verify_oop_mask(); Universe_verify_oop_bits = Universe::verify_oop_bits(); @@ -390,7 +385,6 @@ JVMCIObjectArray CompilerToVM::initialize_intrinsics(JVMCI_TRAPS) { X86_ONLY(do_int_flag(UseAVX)) \ do_bool_flag(UseCRC32Intrinsics) \ do_bool_flag(UseAdler32Intrinsics) \ - do_bool_flag(UseCompressedClassPointers) \ do_bool_flag(UseCompressedOops) \ X86_ONLY(do_bool_flag(UseCountLeadingZerosInstruction)) \ X86_ONLY(do_bool_flag(UseCountTrailingZerosInstruction)) \ diff --git a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp index 74314b0ad61..1fdf98588fd 100644 --- a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp +++ b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -342,7 +342,7 @@ volatile_nonstatic_field(ObjectMonitor, _succ, int64_t) \ \ volatile_nonstatic_field(oopDesc, _mark, markWord) \ - volatile_nonstatic_field(oopDesc, _metadata._klass, Klass*) \ + volatile_nonstatic_field(oopDesc, _compressed_klass, narrowKlass) \ \ static_field(StubRoutines, _verify_oop_count, jint) \ \ @@ -450,8 +450,8 @@ nonstatic_field(ThreadLocalAllocBuffer, _pf_top, HeapWord*) \ nonstatic_field(ThreadLocalAllocBuffer, _desired_size, size_t) \ nonstatic_field(ThreadLocalAllocBuffer, _refill_waste_limit, size_t) \ - nonstatic_field(ThreadLocalAllocBuffer, _number_of_refills, unsigned) \ - nonstatic_field(ThreadLocalAllocBuffer, _slow_allocations, unsigned) \ + nonstatic_field(ThreadLocalAllocBuffer, _num_refills, unsigned) \ + nonstatic_field(ThreadLocalAllocBuffer, _num_slow_allocations, unsigned) \ \ nonstatic_field(SafepointMechanism::ThreadData, _polling_word, volatile uintptr_t) \ nonstatic_field(SafepointMechanism::ThreadData, _polling_page, volatile uintptr_t) \ diff --git a/src/hotspot/share/libadt/vectset.cpp b/src/hotspot/share/libadt/vectset.cpp index 176660d8302..bd9e97d9877 100644 --- a/src/hotspot/share/libadt/vectset.cpp +++ b/src/hotspot/share/libadt/vectset.cpp @@ -51,7 +51,7 @@ void VectorSet::grow(uint new_word_capacity) { assert(new_word_capacity < (1U << 30), ""); uint x = next_power_of_2(new_word_capacity); if (x > _data_size) { - _data = REALLOC_ARENA_ARRAY(_set_arena, uint32_t, _data, _size, x); + _data = REALLOC_ARENA_ARRAY(_set_arena, _data, _size, x); _data_size = x; } Copy::zero_to_bytes(_data + _size, (x - _size) * sizeof(uint32_t)); diff --git a/src/hotspot/share/logging/logAsyncWriter.hpp b/src/hotspot/share/logging/logAsyncWriter.hpp index a93b1ca58f6..933482929c7 100644 --- a/src/hotspot/share/logging/logAsyncWriter.hpp +++ b/src/hotspot/share/logging/logAsyncWriter.hpp @@ -124,7 +124,7 @@ class AsyncLogWriter : public NonJavaThread { } ~Buffer() { - FREE_C_HEAP_ARRAY(char, _buf); + FREE_C_HEAP_ARRAY(_buf); } void push_flush_token(); diff --git a/src/hotspot/share/logging/logConfiguration.cpp b/src/hotspot/share/logging/logConfiguration.cpp index b883dbccacf..1ba45cc050f 100644 --- a/src/hotspot/share/logging/logConfiguration.cpp +++ b/src/hotspot/share/logging/logConfiguration.cpp @@ -123,7 +123,7 @@ void LogConfiguration::initialize(jlong vm_start_time) { void LogConfiguration::finalize() { disable_outputs(); - FREE_C_HEAP_ARRAY(LogOutput*, _outputs); + FREE_C_HEAP_ARRAY(_outputs); } // Normalizes the given LogOutput name to type=name form. @@ -206,7 +206,7 @@ LogOutput* LogConfiguration::new_output(const char* name, size_t LogConfiguration::add_output(LogOutput* output) { size_t idx = _n_outputs++; - _outputs = REALLOC_C_HEAP_ARRAY(LogOutput*, _outputs, _n_outputs, mtLogging); + _outputs = REALLOC_C_HEAP_ARRAY(_outputs, _n_outputs, mtLogging); _outputs[idx] = output; return idx; } @@ -218,7 +218,7 @@ void LogConfiguration::delete_output(size_t idx) { LogOutput* output = _outputs[idx]; // Swap places with the last output and shrink the array _outputs[idx] = _outputs[--_n_outputs]; - _outputs = REALLOC_C_HEAP_ARRAY(LogOutput*, _outputs, _n_outputs, mtLogging); + _outputs = REALLOC_C_HEAP_ARRAY(_outputs, _n_outputs, mtLogging); delete output; } @@ -546,7 +546,7 @@ bool LogConfiguration::parse_log_arguments(const char* outputstr, } } - FREE_C_HEAP_ARRAY(char, normalized); + FREE_C_HEAP_ARRAY(normalized); if (idx == SIZE_MAX) { return false; } @@ -724,8 +724,7 @@ void LogConfiguration::register_update_listener(UpdateListenerFunction cb) { assert(cb != nullptr, "Should not register nullptr as listener"); ConfigurationLock cl; size_t idx = _n_listener_callbacks++; - _listener_callbacks = REALLOC_C_HEAP_ARRAY(UpdateListenerFunction, - _listener_callbacks, + _listener_callbacks = REALLOC_C_HEAP_ARRAY(_listener_callbacks, _n_listener_callbacks, mtLogging); _listener_callbacks[idx] = cb; diff --git a/src/hotspot/share/logging/logFileOutput.cpp b/src/hotspot/share/logging/logFileOutput.cpp index 9d6d19d739e..c805a3e1077 100644 --- a/src/hotspot/share/logging/logFileOutput.cpp +++ b/src/hotspot/share/logging/logFileOutput.cpp @@ -160,8 +160,8 @@ static uint next_file_number(const char* filename, } } - FREE_C_HEAP_ARRAY(char, oldest_name); - FREE_C_HEAP_ARRAY(char, archive_name); + FREE_C_HEAP_ARRAY(oldest_name); + FREE_C_HEAP_ARRAY(archive_name); return next_num; } diff --git a/src/hotspot/share/logging/logMessageBuffer.cpp b/src/hotspot/share/logging/logMessageBuffer.cpp index 8f308b1f015..4714cd65f8a 100644 --- a/src/hotspot/share/logging/logMessageBuffer.cpp +++ b/src/hotspot/share/logging/logMessageBuffer.cpp @@ -31,7 +31,7 @@ static void grow(T*& buffer, size_t& capacity, size_t minimum_length = 0) { if (new_size < minimum_length) { new_size = minimum_length; } - buffer = REALLOC_C_HEAP_ARRAY(T, buffer, new_size, mtLogging); + buffer = REALLOC_C_HEAP_ARRAY(buffer, new_size, mtLogging); capacity = new_size; } @@ -48,8 +48,8 @@ LogMessageBuffer::LogMessageBuffer() : _message_buffer_size(0), LogMessageBuffer::~LogMessageBuffer() { if (_allocated) { - FREE_C_HEAP_ARRAY(char, _message_buffer); - FREE_C_HEAP_ARRAY(LogLine, _lines); + FREE_C_HEAP_ARRAY(_message_buffer); + FREE_C_HEAP_ARRAY(_lines); } } diff --git a/src/hotspot/share/logging/logOutput.cpp b/src/hotspot/share/logging/logOutput.cpp index e3c68b49b13..f74e614e539 100644 --- a/src/hotspot/share/logging/logOutput.cpp +++ b/src/hotspot/share/logging/logOutput.cpp @@ -181,7 +181,7 @@ static void add_selections(LogSelection** selections, // Ensure there's enough room for both wildcard_match and exact_match if (*n_selections + 2 > *selections_cap) { *selections_cap *= 2; - *selections = REALLOC_C_HEAP_ARRAY(LogSelection, *selections, *selections_cap, mtLogging); + *selections = REALLOC_C_HEAP_ARRAY(*selections, *selections_cap, mtLogging); } // Add found matching selections to the result array @@ -317,8 +317,8 @@ void LogOutput::update_config_string(const size_t on_level[LogLevel::Count]) { break; } } - FREE_C_HEAP_ARRAY(LogTagSet*, deviates); - FREE_C_HEAP_ARRAY(Selection, selections); + FREE_C_HEAP_ARRAY(deviates); + FREE_C_HEAP_ARRAY(selections); } bool LogOutput::parse_options(const char* options, outputStream* errstream) { diff --git a/src/hotspot/share/logging/logTag.hpp b/src/hotspot/share/logging/logTag.hpp index 20d61b542b0..2b8d6a72be4 100644 --- a/src/hotspot/share/logging/logTag.hpp +++ b/src/hotspot/share/logging/logTag.hpp @@ -96,6 +96,7 @@ class outputStream; LOG_TAG(heap) \ LOG_TAG(heapdump) \ NOT_PRODUCT(LOG_TAG(heapsampling)) \ + COMPILER2_PRESENT(LOG_TAG(hotcode)) \ LOG_TAG(humongous) \ LOG_TAG(ihop) \ LOG_TAG(iklass) \ diff --git a/src/hotspot/share/logging/logTagSet.cpp b/src/hotspot/share/logging/logTagSet.cpp index 667c76ec306..28b0d697b14 100644 --- a/src/hotspot/share/logging/logTagSet.cpp +++ b/src/hotspot/share/logging/logTagSet.cpp @@ -215,5 +215,5 @@ void LogTagSet::list_all_tagsets(outputStream* out) { os::free(tagset_labels[idx]); } out->cr(); - FREE_C_HEAP_ARRAY(char*, tagset_labels); + FREE_C_HEAP_ARRAY(tagset_labels); } diff --git a/src/hotspot/share/memory/allocation.hpp b/src/hotspot/share/memory/allocation.hpp index 15b7750a363..d43add7cb66 100644 --- a/src/hotspot/share/memory/allocation.hpp +++ b/src/hotspot/share/memory/allocation.hpp @@ -95,7 +95,7 @@ typedef AllocFailStrategy::AllocFailEnum AllocFailType; // // char* AllocateHeap(size_t size, MemTag mem_tag, const NativeCallStack& stack, AllocFailType alloc_failmode = AllocFailStrategy::EXIT_OOM); // char* AllocateHeap(size_t size, MemTag mem_tag, AllocFailType alloc_failmode = AllocFailStrategy::EXIT_OOM); -// char* ReallocateHeap(char *old, size_t size, MemTag mem_tag, AllocFailType alloc_failmode = AllocFailStrategy::EXIT_OOM); +// char* ReallocateHeap(char* old, size_t size, MemTag mem_tag, AllocFailType alloc_failmode = AllocFailStrategy::EXIT_OOM); // void FreeHeap(void* p); // @@ -112,7 +112,7 @@ char* AllocateHeap(size_t size, MemTag mem_tag, AllocFailType alloc_failmode = AllocFailStrategy::EXIT_OOM); -char* ReallocateHeap(char *old, +char* ReallocateHeap(char* old, size_t size, MemTag mem_tag, AllocFailType alloc_failmode = AllocFailStrategy::EXIT_OOM); @@ -374,9 +374,9 @@ extern char* resource_allocate_bytes(size_t size, AllocFailType alloc_failmode = AllocFailStrategy::EXIT_OOM); extern char* resource_allocate_bytes(Thread* thread, size_t size, AllocFailType alloc_failmode = AllocFailStrategy::EXIT_OOM); -extern char* resource_reallocate_bytes( char *old, size_t old_size, size_t new_size, +extern char* resource_reallocate_bytes(char* old, size_t old_size, size_t new_size, AllocFailType alloc_failmode = AllocFailStrategy::EXIT_OOM); -extern void resource_free_bytes( Thread* thread, char *old, size_t size ); +extern void resource_free_bytes(Thread* thread, char* obj, size_t size); //---------------------------------------------------------------------- // Base class for objects allocated in the resource area. @@ -479,6 +479,8 @@ protected: #endif // PRODUCT }; +#define REALLOC_RETURN_TYPE(old) typename std::remove_reference::type + // One of the following macros must be used when allocating an array // or object to determine whether it should reside in the C heap on in // the resource area. @@ -495,21 +497,18 @@ protected: #define NEW_RESOURCE_ARRAY_IN_THREAD_RETURN_NULL(thread, type, size)\ (type*) resource_allocate_bytes(thread, (size) * sizeof(type), AllocFailStrategy::RETURN_NULL) -#define REALLOC_RESOURCE_ARRAY(type, old, old_size, new_size)\ - (type*) resource_reallocate_bytes((char*)(old), (old_size) * sizeof(type), (new_size) * sizeof(type)) +#define REALLOC_RESOURCE_ARRAY(old, old_size, new_size)\ + (REALLOC_RETURN_TYPE(old)) resource_reallocate_bytes((char*)(old), (old_size) * sizeof(*old), (new_size) * sizeof(*old)) -#define REALLOC_RESOURCE_ARRAY_RETURN_NULL(type, old, old_size, new_size)\ - (type*) resource_reallocate_bytes((char*)(old), (old_size) * sizeof(type),\ - (new_size) * sizeof(type), AllocFailStrategy::RETURN_NULL) +#define REALLOC_RESOURCE_ARRAY_RETURN_NULL(old, old_size, new_size)\ + (REALLOC_RETURN_TYPE(old)) resource_reallocate_bytes((char*)(old), (old_size) * sizeof(*old), \ + (new_size) * sizeof(*old), AllocFailStrategy::RETURN_NULL) -#define FREE_RESOURCE_ARRAY(type, old, size)\ - resource_free_bytes(Thread::current(), (char*)(old), (size) * sizeof(type)) +#define FREE_RESOURCE_ARRAY(obj, size)\ + resource_free_bytes(Thread::current(), (char*)(obj), (size) * sizeof(*obj)) -#define FREE_RESOURCE_ARRAY_IN_THREAD(thread, type, old, size)\ - resource_free_bytes(thread, (char*)(old), (size) * sizeof(type)) - -#define FREE_FAST(old)\ - /* nop */ +#define FREE_RESOURCE_ARRAY_IN_THREAD(thread, obj, size)\ + resource_free_bytes(thread, (char*)(obj), (size) * sizeof(*obj)) #define NEW_RESOURCE_OBJ(type)\ NEW_RESOURCE_ARRAY(type, 1) @@ -532,14 +531,14 @@ protected: #define NEW_C_HEAP_ARRAY_RETURN_NULL(type, size, mem_tag)\ NEW_C_HEAP_ARRAY2(type, (size), mem_tag, AllocFailStrategy::RETURN_NULL) -#define REALLOC_C_HEAP_ARRAY(type, old, size, mem_tag)\ - (type*) (ReallocateHeap((char*)(old), (size) * sizeof(type), mem_tag)) +#define REALLOC_C_HEAP_ARRAY(old, size, mem_tag)\ + (REALLOC_RETURN_TYPE(old)) ReallocateHeap((char*)(old), (size) * sizeof(*old), mem_tag) -#define REALLOC_C_HEAP_ARRAY_RETURN_NULL(type, old, size, mem_tag)\ - (type*) (ReallocateHeap((char*)(old), (size) * sizeof(type), mem_tag, AllocFailStrategy::RETURN_NULL)) +#define REALLOC_C_HEAP_ARRAY_RETURN_NULL(old, size, mem_tag)\ + (REALLOC_RETURN_TYPE(old)) ReallocateHeap((char*)(old), (size) * sizeof(*old), mem_tag, AllocFailStrategy::RETURN_NULL) -#define FREE_C_HEAP_ARRAY(type, old) \ - FreeHeap((char*)(old)) +#define FREE_C_HEAP_ARRAY(obj) \ + FreeHeap((void*)(obj)) // allocate type in heap without calling ctor #define NEW_C_HEAP_OBJ(type, mem_tag)\ @@ -548,9 +547,9 @@ protected: #define NEW_C_HEAP_OBJ_RETURN_NULL(type, mem_tag)\ NEW_C_HEAP_ARRAY_RETURN_NULL(type, 1, mem_tag) -// deallocate obj of type in heap without calling dtor -#define FREE_C_HEAP_OBJ(objname)\ - FreeHeap((char*)objname); +// deallocate obj in heap without calling dtor +#define FREE_C_HEAP_OBJ(obj)\ + FREE_C_HEAP_ARRAY(obj) //------------------------------ReallocMark--------------------------------- diff --git a/src/hotspot/share/memory/arena.hpp b/src/hotspot/share/memory/arena.hpp index 7d88c79ca52..252e511f615 100644 --- a/src/hotspot/share/memory/arena.hpp +++ b/src/hotspot/share/memory/arena.hpp @@ -240,12 +240,12 @@ private: #define NEW_ARENA_ARRAY(arena, type, size) \ (type*) (arena)->Amalloc((size) * sizeof(type)) -#define REALLOC_ARENA_ARRAY(arena, type, old, old_size, new_size) \ - (type*) (arena)->Arealloc((char*)(old), (old_size) * sizeof(type), \ - (new_size) * sizeof(type) ) +#define REALLOC_ARENA_ARRAY(arena, old, old_size, new_size) \ + (REALLOC_RETURN_TYPE(old)) (arena)->Arealloc((char*)(old), (old_size) * sizeof(*old), \ + (new_size) * sizeof(*old) ) -#define FREE_ARENA_ARRAY(arena, type, old, size) \ - (arena)->Afree((char*)(old), (size) * sizeof(type)) +#define FREE_ARENA_ARRAY(arena, obj, size) \ + (arena)->Afree((char*)(obj), (size) * sizeof(*obj)) #define NEW_ARENA_OBJ(arena, type) \ NEW_ARENA_ARRAY(arena, type, 1) diff --git a/src/hotspot/share/memory/classLoaderMetaspace.cpp b/src/hotspot/share/memory/classLoaderMetaspace.cpp index c1ff172071d..af3b8b1b77f 100644 --- a/src/hotspot/share/memory/classLoaderMetaspace.cpp +++ b/src/hotspot/share/memory/classLoaderMetaspace.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -162,10 +162,12 @@ void ClassLoaderMetaspace::deallocate(MetaWord* ptr, size_t word_size) { MetaBlock bl(ptr, word_size); // Add to class arena only if block is usable for encodable Klass storage. MetaspaceArena* receiving_arena = non_class_space_arena(); - if (Metaspace::using_class_space() && Metaspace::is_in_class_space(ptr) && +#if INCLUDE_CLASS_SPACE + if (Metaspace::is_in_class_space(ptr) && is_aligned(ptr, class_space_arena()->allocation_alignment_bytes())) { receiving_arena = class_space_arena(); } +#endif receiving_arena->deallocate(bl); DEBUG_ONLY(InternalStats::inc_num_deallocs();) } diff --git a/src/hotspot/share/memory/classLoaderMetaspace.hpp b/src/hotspot/share/memory/classLoaderMetaspace.hpp index aa43e171708..dc782611e98 100644 --- a/src/hotspot/share/memory/classLoaderMetaspace.hpp +++ b/src/hotspot/share/memory/classLoaderMetaspace.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2026, 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,9 +40,10 @@ namespace metaspace { // A ClassLoaderMetaspace manages MetaspaceArena(s) for a CLD. // -// A CLD owns one MetaspaceArena if UseCompressedClassPointers is false. Otherwise -// it owns two - one for the Klass* objects from the class space, one for the other -// types of MetaspaceObjs from the non-class space. +// 64-bit: +// +// A CLD owns two MetaspaceArenas - one for the Klass* objects from the class space, +// one for the other types of MetaspaceObjs from the non-class space. // // +------+ +----------------------+ +-------------------+ // | CLD | ---> | ClassLoaderMetaspace | ----> | (non class) Arena | @@ -58,6 +59,11 @@ namespace metaspace { // ^ // alloc top // +// 32-bit: +// +// A CLD owns just one MetaspaceArena. In that arena all metadata - Klass and other - +// are placed. + class ClassLoaderMetaspace : public CHeapObj { friend class metaspace::ClmsTester; // for gtests @@ -67,11 +73,10 @@ class ClassLoaderMetaspace : public CHeapObj { const Metaspace::MetaspaceType _space_type; // Arena for allocations from non-class metaspace - // (resp. for all allocations if -XX:-UseCompressedClassPointers). metaspace::MetaspaceArena* _non_class_space_arena; // Arena for allocations from class space - // (null if -XX:-UseCompressedClassPointers). + // (null for 32-bit). metaspace::MetaspaceArena* _class_space_arena; Mutex* lock() const { return _lock; } diff --git a/src/hotspot/share/memory/heapInspection.cpp b/src/hotspot/share/memory/heapInspection.cpp index aae3c99a634..5abb7e4ddcc 100644 --- a/src/hotspot/share/memory/heapInspection.cpp +++ b/src/hotspot/share/memory/heapInspection.cpp @@ -189,7 +189,7 @@ KlassInfoTable::~KlassInfoTable() { for (int index = 0; index < _num_buckets; index++) { _buckets[index].empty(); } - FREE_C_HEAP_ARRAY(KlassInfoBucket, _buckets); + FREE_C_HEAP_ARRAY(_buckets); _buckets = nullptr; } } diff --git a/src/hotspot/share/memory/memRegion.cpp b/src/hotspot/share/memory/memRegion.cpp index 3c481926025..dde4b0ace9a 100644 --- a/src/hotspot/share/memory/memRegion.cpp +++ b/src/hotspot/share/memory/memRegion.cpp @@ -115,5 +115,5 @@ void MemRegion::destroy_array(MemRegion* array, size_t length) { for (size_t i = 0; i < length; i++) { array[i].~MemRegion(); } - FREE_C_HEAP_ARRAY(MemRegion, array); + FREE_C_HEAP_ARRAY(array); } diff --git a/src/hotspot/share/memory/metaspace.cpp b/src/hotspot/share/memory/metaspace.cpp index e686b324004..da6ebc991c8 100644 --- a/src/hotspot/share/memory/metaspace.cpp +++ b/src/hotspot/share/memory/metaspace.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2017, 2021 SAP SE. All rights reserved. * Copyright (c) 2023, 2025, Red Hat, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -166,33 +166,33 @@ void MetaspaceUtils::print_metaspace_change(const MetaspaceCombinedStats& pre_me // it is a constant (to uninformed users, often confusingly large). For non-class space, it would // be interesting since free chunks can be uncommitted, but for now it is left out. - if (Metaspace::using_class_space()) { - log_info(gc, metaspace)(HEAP_CHANGE_FORMAT" " - HEAP_CHANGE_FORMAT" " - HEAP_CHANGE_FORMAT, - HEAP_CHANGE_FORMAT_ARGS("Metaspace", - pre_meta_values.used(), - pre_meta_values.committed(), - meta_values.used(), - meta_values.committed()), - HEAP_CHANGE_FORMAT_ARGS("NonClass", - pre_meta_values.non_class_used(), - pre_meta_values.non_class_committed(), - meta_values.non_class_used(), - meta_values.non_class_committed()), - HEAP_CHANGE_FORMAT_ARGS("Class", - pre_meta_values.class_used(), - pre_meta_values.class_committed(), - meta_values.class_used(), - meta_values.class_committed())); - } else { - log_info(gc, metaspace)(HEAP_CHANGE_FORMAT, - HEAP_CHANGE_FORMAT_ARGS("Metaspace", - pre_meta_values.used(), - pre_meta_values.committed(), - meta_values.used(), - meta_values.committed())); - } +#if INCLUDE_CLASS_SPACE + log_info(gc, metaspace)(HEAP_CHANGE_FORMAT" " + HEAP_CHANGE_FORMAT" " + HEAP_CHANGE_FORMAT, + HEAP_CHANGE_FORMAT_ARGS("Metaspace", + pre_meta_values.used(), + pre_meta_values.committed(), + meta_values.used(), + meta_values.committed()), + HEAP_CHANGE_FORMAT_ARGS("NonClass", + pre_meta_values.non_class_used(), + pre_meta_values.non_class_committed(), + meta_values.non_class_used(), + meta_values.non_class_committed()), + HEAP_CHANGE_FORMAT_ARGS("Class", + pre_meta_values.class_used(), + pre_meta_values.class_committed(), + meta_values.class_used(), + meta_values.class_committed())); +#else + log_info(gc, metaspace)(HEAP_CHANGE_FORMAT, + HEAP_CHANGE_FORMAT_ARGS("Metaspace", + pre_meta_values.used(), + pre_meta_values.committed(), + meta_values.used(), + meta_values.committed())); +#endif // INCLUDE_CLASS_SPACE } // This will print out a basic metaspace usage report but @@ -226,41 +226,36 @@ void MetaspaceUtils::print_on(outputStream* out) { stats.committed()/K, stats.reserved()/K); - if (Metaspace::using_class_space()) { - StreamIndentor si(out, 1); - out->print("class space "); - out->fill_to(17); - out->print_cr("used %zuK, " - "committed %zuK, " - "reserved %zuK", - stats.class_space_stats().used()/K, - stats.class_space_stats().committed()/K, - stats.class_space_stats().reserved()/K); - } +#if INCLUDE_CLASS_SPACE + StreamIndentor si(out, 1); + out->print("class space "); + out->fill_to(17); + out->print_cr("used %zuK, " + "committed %zuK, " + "reserved %zuK", + stats.class_space_stats().used()/K, + stats.class_space_stats().committed()/K, + stats.class_space_stats().reserved()/K); +#endif // INCLUDE_CLASS_SPACE } #ifdef ASSERT void MetaspaceUtils::verify() { if (Metaspace::initialized()) { - // Verify non-class chunkmanager... ChunkManager* cm = ChunkManager::chunkmanager_nonclass(); cm->verify(); - // ... and space list. VirtualSpaceList* vsl = VirtualSpaceList::vslist_nonclass(); vsl->verify(); - if (Metaspace::using_class_space()) { - // If we use compressed class pointers, verify class chunkmanager... - cm = ChunkManager::chunkmanager_class(); - cm->verify(); - - // ... and class spacelist. - vsl = VirtualSpaceList::vslist_class(); - vsl->verify(); - } +#if INCLUDE_CLASS_SPACE + cm = ChunkManager::chunkmanager_class(); + cm->verify(); + vsl = VirtualSpaceList::vslist_class(); + vsl->verify(); +#endif // INCLUDE_CLASS_SPACE } } #endif @@ -387,7 +382,8 @@ void MetaspaceGC::post_initialize() { bool MetaspaceGC::can_expand(size_t word_size, bool is_class) { // Check if the compressed class space is full. - if (is_class && Metaspace::using_class_space()) { +#if INCLUDE_CLASS_SPACE + if (is_class) { size_t class_committed = MetaspaceUtils::committed_bytes(Metaspace::ClassType); if (class_committed + word_size * BytesPerWord > CompressedClassSpaceSize) { log_trace(gc, metaspace, freelist)("Cannot expand %s metaspace by %zu words (CompressedClassSpaceSize = %zu words)", @@ -395,6 +391,7 @@ bool MetaspaceGC::can_expand(size_t word_size, bool is_class) { return false; } } +#endif // INCLUDE_CLASS_SPACE // Check if the user has imposed a limit on the metaspace memory. size_t committed_bytes = MetaspaceUtils::committed_bytes(); @@ -548,7 +545,7 @@ const void* Metaspace::_class_space_end = nullptr; bool Metaspace::initialized() { return metaspace::MetaspaceContext::context_nonclass() != nullptr - LP64_ONLY(&& (using_class_space() ? Metaspace::class_space_is_initialized() : true)); + CLASS_SPACE_ONLY(&& Metaspace::class_space_is_initialized()); } #ifdef _LP64 @@ -566,9 +563,9 @@ void Metaspace::print_compressed_class_space(outputStream* st) { // Given a prereserved space, use that to set up the compressed class space list. void Metaspace::initialize_class_space(ReservedSpace rs) { + STATIC_ASSERT(INCLUDE_CLASS_SPACE == 1); assert(rs.size() >= CompressedClassSpaceSize, "%zu != %zu", rs.size(), CompressedClassSpaceSize); - assert(using_class_space(), "Must be using class space"); assert(rs.size() == CompressedClassSpaceSize, "%zu != %zu", rs.size(), CompressedClassSpaceSize); @@ -658,49 +655,51 @@ void Metaspace::ergo_initialize() { MaxMetaspaceSize = MAX2(MaxMetaspaceSize, commit_alignment()); - if (UseCompressedClassPointers) { - // Let Class Space not be larger than 80% of MaxMetaspaceSize. Note that is - // grossly over-dimensioned for most usage scenarios; typical ratio of - // class space : non class space usage is about 1:6. With many small classes, - // it can get as low as 1:2. It is not a big deal though since ccs is only - // reserved and will be committed on demand only. - const size_t max_ccs_size = 8 * (MaxMetaspaceSize / 10); +#if INCLUDE_CLASS_SPACE - // Sanity check. - const size_t max_klass_range = CompressedKlassPointers::max_klass_range_size(); - assert(max_klass_range >= reserve_alignment(), - "Klass range (%zu) must cover at least a full root chunk (%zu)", - max_klass_range, reserve_alignment()); + // Let Class Space not be larger than 80% of MaxMetaspaceSize. Note that is + // grossly over-dimensioned for most usage scenarios; typical ratio of + // class space : non class space usage is about 1:6. With many small classes, + // it can get as low as 1:2. It is not a big deal though since ccs is only + // reserved and will be committed on demand only. + const size_t max_ccs_size = 8 * (MaxMetaspaceSize / 10); - size_t adjusted_ccs_size = MIN3(CompressedClassSpaceSize, max_ccs_size, max_klass_range); + // Sanity check. + const size_t max_klass_range = CompressedKlassPointers::max_klass_range_size(); + assert(max_klass_range >= reserve_alignment(), + "Klass range (%zu) must cover at least a full root chunk (%zu)", + max_klass_range, reserve_alignment()); - // CCS must be aligned to root chunk size, and be at least the size of one - // root chunk. - adjusted_ccs_size = align_up(adjusted_ccs_size, reserve_alignment()); - adjusted_ccs_size = MAX2(adjusted_ccs_size, reserve_alignment()); + size_t adjusted_ccs_size = MIN3(CompressedClassSpaceSize, max_ccs_size, max_klass_range); - // Print a warning if the adjusted size differs from the users input - if (CompressedClassSpaceSize != adjusted_ccs_size) { - #define X "CompressedClassSpaceSize adjusted from user input " \ - "%zu bytes to %zu bytes", CompressedClassSpaceSize, adjusted_ccs_size - if (FLAG_IS_CMDLINE(CompressedClassSpaceSize)) { - log_warning(metaspace)(X); - } else { - log_info(metaspace)(X); - } - #undef X - } - - // Note: re-adjusting may have us left with a CompressedClassSpaceSize - // larger than MaxMetaspaceSize for very small values of MaxMetaspaceSize. - // Lets just live with that, its not a big deal. - if (adjusted_ccs_size != CompressedClassSpaceSize) { - FLAG_SET_ERGO(CompressedClassSpaceSize, adjusted_ccs_size); - log_info(metaspace)("Setting CompressedClassSpaceSize to %zu.", - CompressedClassSpaceSize); + // CCS must be aligned to root chunk size, and be at least the size of one + // root chunk. + adjusted_ccs_size = align_up(adjusted_ccs_size, reserve_alignment()); + adjusted_ccs_size = MAX2(adjusted_ccs_size, reserve_alignment()); + + // Print a warning if the adjusted size differs from the users input + if (CompressedClassSpaceSize != adjusted_ccs_size) { + #define X "CompressedClassSpaceSize adjusted from user input " \ + "%zu bytes to %zu bytes", CompressedClassSpaceSize, adjusted_ccs_size + if (FLAG_IS_CMDLINE(CompressedClassSpaceSize)) { + log_warning(metaspace)(X); + } else { + log_info(metaspace)(X); } + #undef X } + // Note: re-adjusting may have us left with a CompressedClassSpaceSize + // larger than MaxMetaspaceSize for very small values of MaxMetaspaceSize. + // Lets just live with that, its not a big deal. + if (adjusted_ccs_size != CompressedClassSpaceSize) { + FLAG_SET_ERGO(CompressedClassSpaceSize, adjusted_ccs_size); + log_info(metaspace)("Setting CompressedClassSpaceSize to %zu.", + CompressedClassSpaceSize); + } + +#endif // INCLUDE_CLASS_SPACE + // Set MetaspaceSize, MinMetaspaceExpansion and MaxMetaspaceExpansion if (MetaspaceSize > MaxMetaspaceSize) { MetaspaceSize = MaxMetaspaceSize; @@ -724,15 +723,12 @@ void Metaspace::global_initialize() { AOTMetaspace::initialize_for_static_dump(); } - // If UseCompressedClassPointers=1, we have two cases: + // We have two cases: // a) if CDS is active (runtime, Xshare=on), it will create the class space - // for us, initialize it and set up CompressedKlassPointers encoding. - // Class space will be reserved above the mapped archives. + // for us. It then will set up encoding to cover both CDS archive space and class space. // b) if CDS either deactivated (Xshare=off) or a static dump is to be done (Xshare:dump), - // we will create the class space on our own. It will be placed above the java heap, - // since we assume it has been placed in low - // address regions. We may rethink this (see JDK-8244943). Failing that, - // it will be placed anywhere. + // we will create the class space on our own and set up encoding to only cover the + // class space. #if INCLUDE_CDS // case (a) @@ -746,9 +742,9 @@ void Metaspace::global_initialize() { } #endif // INCLUDE_CDS -#ifdef _LP64 +#if INCLUDE_CLASS_SPACE - if (using_class_space() && !class_space_is_initialized()) { + if (!class_space_is_initialized()) { assert(!CDSConfig::is_using_archive(), "CDS archive is not mapped at this point"); // case (b) (No CDS) @@ -835,28 +831,23 @@ void Metaspace::global_initialize() { } #else - // +UseCompressedClassPointers on 32-bit: does not need class space. Klass can live wherever. - if (UseCompressedClassPointers) { - const address start = (address)os::vm_min_address(); // but not in the zero page - const address end = (address)CompressedKlassPointers::max_klass_range_size(); - CompressedKlassPointers::initialize(start, end - start); - } -#endif // __LP64 + // 32-bit: + const address start = (address)os::vm_min_address(); // but not in the zero page + const address end = (address)CompressedKlassPointers::max_klass_range_size(); + CompressedKlassPointers::initialize(start, end - start); +#endif // INCLUDE_CLASS_SPACE // Initialize non-class virtual space list, and its chunk manager: MetaspaceContext::initialize_nonclass_space_context(); _tracer = new MetaspaceTracer(); - if (UseCompressedClassPointers) { - // Note: "cds" would be a better fit but keep this for backward compatibility. - LogTarget(Info, gc, metaspace) lt; - if (lt.is_enabled()) { - LogStream ls(lt); - CDS_ONLY(AOTMetaspace::print_on(&ls);) - Metaspace::print_compressed_class_space(&ls); - CompressedKlassPointers::print_mode(&ls); - } + LogTarget(Info, gc, metaspace) lt; + if (lt.is_enabled()) { + LogStream ls(lt); + CDS_ONLY(AOTMetaspace::print_on(&ls);) + Metaspace::print_compressed_class_space(&ls); + CompressedKlassPointers::print_mode(&ls); } } @@ -888,15 +879,13 @@ MetaWord* Metaspace::allocate(ClassLoaderData* loader_data, size_t word_size, MetaWord* result = loader_data->metaspace_non_null()->allocate(word_size, mdtype); if (result != nullptr) { -#ifdef ASSERT - if (using_class_space() && mdtype == ClassType) { + if (INCLUDE_CLASS_SPACE == 1 && mdtype == ClassType) { assert(is_in_class_space(result) && is_aligned(result, CompressedKlassPointers::klass_alignment_in_bytes()), "Sanity"); } else { assert((is_in_class_space(result) || is_in_nonclass_metaspace(result)) && is_aligned(result, Metaspace::min_allocation_alignment_bytes), "Sanity"); } -#endif // Zero initialize. Copy::fill_to_words((HeapWord*)result, word_size, 0); log_trace(metaspace)("Metaspace::allocate: type %d return " PTR_FORMAT ".", (int)type, p2i(result)); @@ -1017,12 +1006,12 @@ void Metaspace::purge(bool classes_unloaded) { if (cm != nullptr) { cm->purge(); } - if (using_class_space()) { - cm = ChunkManager::chunkmanager_class(); - if (cm != nullptr) { - cm->purge(); - } +#if INCLUDE_CLASS_SPACE + cm = ChunkManager::chunkmanager_class(); + if (cm != nullptr) { + cm->purge(); } +#endif // INCLUDE_CLASS_SPACE } // Try to satisfy queued metaspace allocation requests. diff --git a/src/hotspot/share/memory/metaspace.hpp b/src/hotspot/share/memory/metaspace.hpp index 01ef4b4dd49..96f5a2459ce 100644 --- a/src/hotspot/share/memory/metaspace.hpp +++ b/src/hotspot/share/memory/metaspace.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2017, 2021 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -164,18 +164,12 @@ public: static void print_compressed_class_space(outputStream* st) NOT_LP64({}); - // Return TRUE only if UseCompressedClassPointers is True. - static bool using_class_space() { - return NOT_LP64(false) LP64_ONLY(UseCompressedClassPointers); - } - static bool is_class_space_allocation(MetadataType mdType) { - return mdType == ClassType && using_class_space(); + return CLASS_SPACE_ONLY(mdType == ClassType) NOT_CLASS_SPACE(false); } static bool initialized(); }; - #endif // SHARE_MEMORY_METASPACE_HPP diff --git a/src/hotspot/share/memory/metaspace/metaspaceReporter.cpp b/src/hotspot/share/memory/metaspace/metaspaceReporter.cpp index 3cff2a50d03..f6683f50fd1 100644 --- a/src/hotspot/share/memory/metaspace/metaspaceReporter.cpp +++ b/src/hotspot/share/memory/metaspace/metaspaceReporter.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018, 2020 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -59,39 +59,39 @@ static void print_vs(outputStream* out, size_t scale) { const size_t committed_nc = RunningCounters::committed_words_nonclass(); const int num_nodes_nc = VirtualSpaceList::vslist_nonclass()->num_nodes(); - if (Metaspace::using_class_space()) { - const size_t reserved_c = RunningCounters::reserved_words_class(); - const size_t committed_c = RunningCounters::committed_words_class(); - const int num_nodes_c = VirtualSpaceList::vslist_class()->num_nodes(); +#if INCLUDE_CLASS_SPACE + const size_t reserved_c = RunningCounters::reserved_words_class(); + const size_t committed_c = RunningCounters::committed_words_class(); + const int num_nodes_c = VirtualSpaceList::vslist_class()->num_nodes(); - out->print(" Non-class space: "); - print_scaled_words(out, reserved_nc, scale, 7); - out->print(" reserved, "); - print_scaled_words_and_percentage(out, committed_nc, reserved_nc, scale, 7); - out->print(" committed, "); - out->print(" %d nodes.", num_nodes_nc); - out->cr(); - out->print(" Class space: "); - print_scaled_words(out, reserved_c, scale, 7); - out->print(" reserved, "); - print_scaled_words_and_percentage(out, committed_c, reserved_c, scale, 7); - out->print(" committed, "); - out->print(" %d nodes.", num_nodes_c); - out->cr(); - out->print(" Both: "); - print_scaled_words(out, reserved_c + reserved_nc, scale, 7); - out->print(" reserved, "); - print_scaled_words_and_percentage(out, committed_c + committed_nc, reserved_c + reserved_nc, scale, 7); - out->print(" committed. "); - out->cr(); - } else { - print_scaled_words(out, reserved_nc, scale, 7); - out->print(" reserved, "); - print_scaled_words_and_percentage(out, committed_nc, reserved_nc, scale, 7); - out->print(" committed, "); - out->print(" %d nodes.", num_nodes_nc); - out->cr(); - } + out->print(" Non-class space: "); + print_scaled_words(out, reserved_nc, scale, 7); + out->print(" reserved, "); + print_scaled_words_and_percentage(out, committed_nc, reserved_nc, scale, 7); + out->print(" committed, "); + out->print(" %d nodes.", num_nodes_nc); + out->cr(); + out->print(" Class space: "); + print_scaled_words(out, reserved_c, scale, 7); + out->print(" reserved, "); + print_scaled_words_and_percentage(out, committed_c, reserved_c, scale, 7); + out->print(" committed, "); + out->print(" %d nodes.", num_nodes_c); + out->cr(); + out->print(" Both: "); + print_scaled_words(out, reserved_c + reserved_nc, scale, 7); + out->print(" reserved, "); + print_scaled_words_and_percentage(out, committed_c + committed_nc, reserved_c + reserved_nc, scale, 7); + out->print(" committed. "); + out->cr(); +#else + print_scaled_words(out, reserved_nc, scale, 7); + out->print(" reserved, "); + print_scaled_words_and_percentage(out, committed_nc, reserved_nc, scale, 7); + out->print(" committed, "); + out->print(" %d nodes.", num_nodes_nc); + out->cr(); +#endif // INCLUDE_CLASS_SPACE } static void print_settings(outputStream* out, size_t scale) { @@ -102,12 +102,12 @@ static void print_settings(outputStream* out, size_t scale) { print_human_readable_size(out, MaxMetaspaceSize, scale); } out->cr(); - if (Metaspace::using_class_space()) { - out->print("CompressedClassSpaceSize: "); - print_human_readable_size(out, CompressedClassSpaceSize, scale); - } else { - out->print("No class space"); - } +#if INCLUDE_CLASS_SPACE + out->print("CompressedClassSpaceSize: "); + print_human_readable_size(out, CompressedClassSpaceSize, scale); +#else + out->print("No class space"); +#endif // INCLUDE_CLASS_SPACE out->cr(); out->print("Initial GC threshold: "); print_human_readable_size(out, MetaspaceSize, scale); @@ -117,9 +117,7 @@ static void print_settings(outputStream* out, size_t scale) { out->cr(); out->print_cr("CDS: %s", (CDSConfig::is_using_archive() ? "on" : (CDSConfig::is_dumping_static_archive() ? "dump" : "off"))); Settings::print_on(out); -#ifdef _LP64 CompressedKlassPointers::print_mode(out); -#endif } // This will print out a basic metaspace usage report but @@ -131,9 +129,7 @@ void MetaspaceReporter::print_basic_report(outputStream* out, size_t scale) { } out->cr(); out->print_cr("Usage:"); - if (Metaspace::using_class_space()) { - out->print(" Non-class: "); - } + CLASS_SPACE_ONLY(out->print(" Non-class: ");) // Note: since we want to purely rely on counters, without any locking or walking the CLDG, // for Usage stats (statistics over in-use chunks) all we can print is the @@ -144,37 +140,35 @@ void MetaspaceReporter::print_basic_report(outputStream* out, size_t scale) { print_scaled_words(out, used_nc, scale, 5); out->print(" used."); out->cr(); - if (Metaspace::using_class_space()) { - const size_t used_c = MetaspaceUtils::used_words(Metaspace::ClassType); - out->print(" Class: "); - print_scaled_words(out, used_c, scale, 5); - out->print(" used."); - out->cr(); - out->print(" Both: "); - const size_t used = used_nc + used_c; - print_scaled_words(out, used, scale, 5); - out->print(" used."); - out->cr(); - } +#if INCLUDE_CLASS_SPACE + const size_t used_c = MetaspaceUtils::used_words(Metaspace::ClassType); + out->print(" Class: "); + print_scaled_words(out, used_c, scale, 5); + out->print(" used."); + out->cr(); + out->print(" Both: "); + const size_t used = used_nc + used_c; + print_scaled_words(out, used, scale, 5); + out->print(" used."); + out->cr(); +#endif // INCLUDE_CLASS_SPACE out->cr(); out->print_cr("Virtual space:"); print_vs(out, scale); out->cr(); out->print_cr("Chunk freelists:"); - if (Metaspace::using_class_space()) { - out->print(" Non-Class: "); - } + CLASS_SPACE_ONLY(out->print(" Non-Class: ");) print_scaled_words(out, ChunkManager::chunkmanager_nonclass()->total_word_size(), scale); out->cr(); - if (Metaspace::using_class_space()) { - out->print(" Class: "); - print_scaled_words(out, ChunkManager::chunkmanager_class()->total_word_size(), scale); - out->cr(); - out->print(" Both: "); - print_scaled_words(out, ChunkManager::chunkmanager_nonclass()->total_word_size() + - ChunkManager::chunkmanager_class()->total_word_size(), scale); - out->cr(); - } +#if INCLUDE_CLASS_SPACE + out->print(" Class: "); + print_scaled_words(out, ChunkManager::chunkmanager_class()->total_word_size(), scale); + out->cr(); + out->print(" Both: "); + print_scaled_words(out, ChunkManager::chunkmanager_nonclass()->total_word_size() + + ChunkManager::chunkmanager_class()->total_word_size(), scale); + out->cr(); +#endif // INCLUDE_CLASS_SPACE out->cr(); // Print basic settings @@ -256,70 +250,70 @@ void MetaspaceReporter::print_report(outputStream* out, size_t scale, int flags) // -- Print VirtualSpaceList details. if ((flags & (int)Option::ShowVSList) > 0) { out->cr(); - out->print_cr("Virtual space list%s:", Metaspace::using_class_space() ? "s" : ""); - - if (Metaspace::using_class_space()) { - out->print_cr(" Non-Class:"); - } +#if INCLUDE_CLASS_SPACE + out->print_cr("Virtual space lists:"); + out->print_cr(" Non-Class:"); VirtualSpaceList::vslist_nonclass()->print_on(out); out->cr(); - if (Metaspace::using_class_space()) { - out->print_cr(" Class:"); - VirtualSpaceList::vslist_class()->print_on(out); - out->cr(); - } + out->print_cr(" Class:"); + VirtualSpaceList::vslist_class()->print_on(out); + out->cr(); +#else + out->print_cr("Virtual space list:"); + VirtualSpaceList::vslist_nonclass()->print_on(out); + out->cr(); +#endif // INCLUDE_CLASS_SPACE } out->cr(); //////////// Freelists (ChunkManager) section /////////////////////////// - out->cr(); - out->print_cr("Chunk freelist%s:", Metaspace::using_class_space() ? "s" : ""); - ChunkManagerStats non_class_cm_stat; ChunkManagerStats class_cm_stat; ChunkManagerStats total_cm_stat; ChunkManager::chunkmanager_nonclass()->add_to_statistics(&non_class_cm_stat); - if (Metaspace::using_class_space()) { - ChunkManager::chunkmanager_nonclass()->add_to_statistics(&non_class_cm_stat); - ChunkManager::chunkmanager_class()->add_to_statistics(&class_cm_stat); - total_cm_stat.add(non_class_cm_stat); - total_cm_stat.add(class_cm_stat); +#if INCLUDE_CLASS_SPACE + ChunkManager::chunkmanager_class()->add_to_statistics(&class_cm_stat); + total_cm_stat.add(non_class_cm_stat); + total_cm_stat.add(class_cm_stat); - out->print_cr(" Non-Class:"); - non_class_cm_stat.print_on(out, scale); - out->cr(); - out->print_cr(" Class:"); - class_cm_stat.print_on(out, scale); - out->cr(); - out->print_cr(" Both:"); - total_cm_stat.print_on(out, scale); - out->cr(); - } else { - ChunkManager::chunkmanager_nonclass()->add_to_statistics(&non_class_cm_stat); - non_class_cm_stat.print_on(out, scale); - out->cr(); - } + out->print_cr("Chunk freelists:"); + out->cr(); + out->print_cr(" Non-Class:"); + non_class_cm_stat.print_on(out, scale); + out->cr(); + out->print_cr(" Class:"); + class_cm_stat.print_on(out, scale); + out->cr(); + out->print_cr(" Both:"); + total_cm_stat.print_on(out, scale); + out->cr(); +#else + out->print_cr("Chunk freelist:"); + ChunkManager::chunkmanager_nonclass()->add_to_statistics(&non_class_cm_stat); + non_class_cm_stat.print_on(out, scale); + out->cr(); +#endif // INCLUDE_CLASS_SPACE // -- Print Chunkmanager details. if ((flags & (int)Option::ShowChunkFreeList) > 0) { out->cr(); out->print_cr("Chunk freelist details:"); - if (Metaspace::using_class_space()) { - out->print_cr(" Non-Class:"); - } +#if INCLUDE_CLASS_SPACE + out->print_cr(" Non-Class:"); ChunkManager::chunkmanager_nonclass()->print_on(out); out->cr(); - if (Metaspace::using_class_space()) { - out->print_cr(" Class:"); - ChunkManager::chunkmanager_class()->print_on(out); - out->cr(); - } + out->print_cr(" Class:"); + ChunkManager::chunkmanager_class()->print_on(out); + out->cr(); +#else + ChunkManager::chunkmanager_nonclass()->print_on(out); + out->cr(); +#endif // INCLUDE_CLASS_SPACE } out->cr(); - //////////// Waste section /////////////////////////// // As a convenience, print a summary of common waste. out->cr(); diff --git a/src/hotspot/share/memory/metaspace/metaspaceStatistics.cpp b/src/hotspot/share/memory/metaspace/metaspaceStatistics.cpp index aab46d64db5..d90e8ed090d 100644 --- a/src/hotspot/share/memory/metaspace/metaspaceStatistics.cpp +++ b/src/hotspot/share/memory/metaspace/metaspaceStatistics.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018, 2020 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -205,28 +205,26 @@ ArenaStats ClmsStats::totals() const { void ClmsStats::print_on(outputStream* st, size_t scale, bool detailed) const { StreamIndentor si(st, 2); st->cr(); - if (Metaspace::using_class_space()) { - st->print("Non-Class: "); - } + CLASS_SPACE_ONLY(st->print("Non-Class: ");) _arena_stats_nonclass.print_on(st, scale, detailed); if (detailed) { st->cr(); } - if (Metaspace::using_class_space()) { +#if INCLUDE_CLASS_SPACE + st->cr(); + st->print(" Class: "); + _arena_stats_class.print_on(st, scale, detailed); + if (detailed) { st->cr(); - st->print(" Class: "); - _arena_stats_class.print_on(st, scale, detailed); - if (detailed) { - st->cr(); - } - st->cr(); - st->print(" Both: "); - totals().print_on(st, scale, detailed); - if (detailed) { - st->cr(); - } } st->cr(); + st->print(" Both: "); + totals().print_on(st, scale, detailed); + if (detailed) { + st->cr(); + } +#endif // INCLUDE_CLASS_SPACE + st->cr(); } #ifdef ASSERT diff --git a/src/hotspot/share/memory/metaspace/rootChunkArea.cpp b/src/hotspot/share/memory/metaspace/rootChunkArea.cpp index a178e122788..39407b46999 100644 --- a/src/hotspot/share/memory/metaspace/rootChunkArea.cpp +++ b/src/hotspot/share/memory/metaspace/rootChunkArea.cpp @@ -473,7 +473,7 @@ RootChunkAreaLUT::~RootChunkAreaLUT() { for (int i = 0; i < _num; i++) { _arr[i].~RootChunkArea(); } - FREE_C_HEAP_ARRAY(RootChunkArea, _arr); + FREE_C_HEAP_ARRAY(_arr); } #ifdef ASSERT diff --git a/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp b/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp index df4e507b104..a934a628582 100644 --- a/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp +++ b/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018, 2021 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -259,12 +259,11 @@ VirtualSpaceNode* VirtualSpaceNode::create_node(size_t word_size, } #ifndef _LP64 - // On 32-bit, with +UseCompressedClassPointers, the whole address space is the encoding range. We therefore - // don't need a class space. However, as a pragmatic workaround for pesty overflow problems on 32-bit, we leave - // a small area at the end of the address space out of the encoding range. We just assume no Klass will ever live + // On 32-bit, the whole address space is the encoding range. We therefore don't need a class space. + // However, as a pragmatic workaround for pesty overflow problems on 32-bit, we leave a small area + // at the end of the address space out of the encoding range. We just assume no Klass will ever live // there (it won't, for no OS we support on 32-bit has user-addressable memory up there). - assert(!UseCompressedClassPointers || - rs.end() <= (char*)CompressedKlassPointers::max_klass_range_size(), "Weirdly high address"); + assert(rs.end() <= (char*)CompressedKlassPointers::max_klass_range_size(), "Weirdly high address"); #endif // _LP64 MemTracker::record_virtual_memory_tag(rs, mtMetaspace); diff --git a/src/hotspot/share/memory/metaspaceClosure.cpp b/src/hotspot/share/memory/metaspaceClosure.cpp index 0239eadf692..0926b55b9a3 100644 --- a/src/hotspot/share/memory/metaspaceClosure.cpp +++ b/src/hotspot/share/memory/metaspaceClosure.cpp @@ -22,11 +22,11 @@ * */ -#include "cds/aotGrowableArray.hpp" #include "classfile/packageEntry.hpp" #include "memory/metaspaceClosure.hpp" #include "oops/array.hpp" #include "oops/instanceKlass.hpp" +#include "utilities/growableArray.hpp" // Sanity checks static_assert(!HAS_METASPACE_POINTERS_DO(int)); @@ -35,8 +35,6 @@ static_assert(HAS_METASPACE_POINTERS_DO(Array)); static_assert(HAS_METASPACE_POINTERS_DO(Array)); static_assert(HAS_METASPACE_POINTERS_DO(InstanceKlass)); static_assert(HAS_METASPACE_POINTERS_DO(PackageEntry)); -static_assert(HAS_METASPACE_POINTERS_DO(AOTGrowableArray)); -static_assert(HAS_METASPACE_POINTERS_DO(AOTGrowableArray)); void MetaspaceClosure::push_impl(MetaspaceClosure::Ref* ref) { if (_enclosing_ref != nullptr) { diff --git a/src/hotspot/share/memory/metaspaceClosure.hpp b/src/hotspot/share/memory/metaspaceClosure.hpp index b6ba69d6f63..ac42dd13c6c 100644 --- a/src/hotspot/share/memory/metaspaceClosure.hpp +++ b/src/hotspot/share/memory/metaspaceClosure.hpp @@ -25,7 +25,6 @@ #ifndef SHARE_MEMORY_METASPACECLOSURE_HPP #define SHARE_MEMORY_METASPACECLOSURE_HPP -#include "cds/aotGrowableArray.hpp" #include "cppstdlib/type_traits.hpp" #include "logging/log.hpp" #include "memory/allocation.hpp" @@ -90,7 +89,9 @@ public: // int size_in_heapwords() const; // // Currently, the iterable types include all subtypes of MetsapceObj, as well - // as GrowableArray, ModuleEntry and PackageEntry. + // as GrowableArray (C-heap allocated only), ModuleEntry, and PackageEntry. + // + // (Note that GrowableArray is supported specially and does not require the above functions.) // // Calling these functions would be trivial if these were virtual functions. // However, to save space, MetaspaceObj has NO vtable. The vtable is introduced @@ -303,11 +304,38 @@ private: }; //-------------------------------- - // Support for AOTGrowableArray + // Support for GrowableArray //-------------------------------- + // GrowableArrayRef -- iterate an instance of GrowableArray. + template class GrowableArrayRef : public Ref { + GrowableArray** _mpp; + GrowableArray* dereference() const { + return *_mpp; + } + + public: + GrowableArrayRef(GrowableArray** mpp, Writability w) : Ref(w), _mpp(mpp) {} + + virtual void** mpp() const { + return (void**)_mpp; + } + + virtual void metaspace_pointers_do(MetaspaceClosure *it) const { + GrowableArray* array = dereference(); + log_trace(aot)("Iter(GrowableArray): %p [%d]", array, array->length()); + array->assert_on_C_heap(); + it->push_c_array(array->data_addr(), array->capacity()); + } + + virtual bool is_read_only_by_default() const { return false; } + virtual bool not_null() const { return dereference() != nullptr; } + virtual int size() const { return (int)heap_word_size(sizeof(*dereference())); } + virtual MetaspaceClosureType type() const { return MetaspaceClosureType::GrowableArrayType; } + }; + // Abstract base class for MSOCArrayRef, MSOPointerCArrayRef and OtherCArrayRef. - // These are used for iterating the buffer held by AOTGrowableArray. + // These are used for iterating the buffer held by GrowableArray. template class CArrayRef : public Ref { T** _mpp; int _num_elems; // Number of elements @@ -354,7 +382,7 @@ private: // MSOCArrayRef -- iterate a C array of type T, where T has metaspace_pointer_do(). // We recursively call T::metaspace_pointers_do() for each element in this array. - // This is for supporting AOTGrowableArray. + // This is for supporting GrowableArray. // // E.g., PackageEntry* _pkg_entry_pointers[2]; // a buffer that has 2 PackageEntry objects // ... @@ -377,7 +405,7 @@ private: // MSOPointerCArrayRef -- iterate a C array of type T*, where T has metaspace_pointer_do(). // We recursively call MetaspaceClosure::push() for each pointer in this array. - // This is for supporting AOTGrowableArray. + // This is for supporting GrowableArray. // // E.g., PackageEntry** _pkg_entry_pointers[2]; // a buffer that has 2 PackageEntry pointers // ... @@ -440,11 +468,11 @@ public: // Array*>* a4 = ...; it->push(&a4); => MSOPointerArrayRef // Array* a5 = ...; it->push(&a5); => MSOPointerArrayRef // - // AOTGrowableArrays have a separate "C array" buffer, so they are scanned in two steps: + // GrowableArrays have a separate "C array" buffer, so they are scanned in two steps: // - // AOTGrowableArray* ga1 = ...; it->push(&ga1); => MSORef => OtherCArrayRef - // AOTGrowableArray* ga2 = ...; it->push(&ga2); => MSORef => MSOCArrayRef - // AOTGrowableArray* ga3 = ...; it->push(&ga3); => MSORef => MSOPointerCArrayRef + // GrowableArray* ga1 = ...; it->push(&ga1); => GrowableArrayRef => OtherCArrayRef + // GrowableArray* ga2 = ...; it->push(&ga2); => GrowableArrayRef => MSOCArrayRef + // GrowableArray* ga3 = ...; it->push(&ga3); => GrowableArrayRef => MSOPointerCArrayRef // // Note that the following will fail to compile: // @@ -476,7 +504,12 @@ public: push_with_ref>(mpp, w); } - // --- The buffer of AOTGrowableArray + template + void push(GrowableArray** mpp, Writability w = _default) { + push_with_ref>(mpp, w); + } + + // --- The buffer of GrowableArray template void push_c_array(T** mpp, int num_elems, Writability w = _default) { push_impl(new OtherCArrayRef(mpp, num_elems, w)); diff --git a/src/hotspot/share/memory/resourceArea.cpp b/src/hotspot/share/memory/resourceArea.cpp index bab652c17ea..63f7ed61d17 100644 --- a/src/hotspot/share/memory/resourceArea.cpp +++ b/src/hotspot/share/memory/resourceArea.cpp @@ -70,10 +70,10 @@ extern char* resource_allocate_bytes(Thread* thread, size_t size, AllocFailType return thread->resource_area()->allocate_bytes(size, alloc_failmode); } -extern char* resource_reallocate_bytes( char *old, size_t old_size, size_t new_size, AllocFailType alloc_failmode){ +extern char* resource_reallocate_bytes(char* old, size_t old_size, size_t new_size, AllocFailType alloc_failmode){ return (char*)Thread::current()->resource_area()->Arealloc(old, old_size, new_size, alloc_failmode); } -extern void resource_free_bytes( Thread* thread, char *old, size_t size ) { - thread->resource_area()->Afree(old, size); +extern void resource_free_bytes(Thread* thread, char* obj, size_t size) { + thread->resource_area()->Afree(obj, size); } diff --git a/src/hotspot/share/memory/universe.cpp b/src/hotspot/share/memory/universe.cpp index a78b245cc07..aecaa1cac22 100644 --- a/src/hotspot/share/memory/universe.cpp +++ b/src/hotspot/share/memory/universe.cpp @@ -1256,7 +1256,7 @@ void Universe::initialize_verify_flags() { } token = strtok_r(nullptr, delimiter, &save_ptr); } - FREE_C_HEAP_ARRAY(char, subset_list); + FREE_C_HEAP_ARRAY(subset_list); } bool Universe::should_verify_subset(uint subset) { diff --git a/src/hotspot/share/nmt/memReporter.cpp b/src/hotspot/share/nmt/memReporter.cpp index 27a94ec7bc0..429db6ad31f 100644 --- a/src/hotspot/share/nmt/memReporter.cpp +++ b/src/hotspot/share/nmt/memReporter.cpp @@ -272,9 +272,7 @@ void MemSummaryReporter::report_summary_of_tag(MemTag mem_tag, } else if (mem_tag == mtClass) { // Metadata information report_metadata(Metaspace::NonClassType); - if (Metaspace::using_class_space()) { - report_metadata(Metaspace::ClassType); - } + CLASS_SPACE_ONLY(report_metadata(Metaspace::ClassType);) } out->cr(); } @@ -754,9 +752,9 @@ void MemSummaryDiffReporter::diff_summary_of_tag(MemTag mem_tag, void MemSummaryDiffReporter::print_metaspace_diff(const MetaspaceCombinedStats& current_ms, const MetaspaceCombinedStats& early_ms) const { print_metaspace_diff("Metadata", current_ms.non_class_space_stats(), early_ms.non_class_space_stats()); - if (Metaspace::using_class_space()) { - print_metaspace_diff("Class space", current_ms.class_space_stats(), early_ms.class_space_stats()); - } +#if INCLUDE_CLASS_SPACE + print_metaspace_diff("Class space", current_ms.class_space_stats(), early_ms.class_space_stats()); +#endif } void MemSummaryDiffReporter::print_metaspace_diff(const char* header, diff --git a/src/hotspot/share/nmt/nmtNativeCallStackStorage.cpp b/src/hotspot/share/nmt/nmtNativeCallStackStorage.cpp index 9a2ecd57ecc..bbf93c5d898 100644 --- a/src/hotspot/share/nmt/nmtNativeCallStackStorage.cpp +++ b/src/hotspot/share/nmt/nmtNativeCallStackStorage.cpp @@ -55,7 +55,7 @@ NativeCallStackStorage::NativeCallStackStorage(bool is_detailed_mode, int table_ } } NativeCallStackStorage::~NativeCallStackStorage() { - FREE_C_HEAP_ARRAY(LinkPtr, _table); + FREE_C_HEAP_ARRAY(_table); } NativeCallStackStorage::NativeCallStackStorage(const NativeCallStackStorage& other) diff --git a/src/hotspot/share/nmt/vmatree.cpp b/src/hotspot/share/nmt/vmatree.cpp index 6c5ab691f6d..1fc05133d22 100644 --- a/src/hotspot/share/nmt/vmatree.cpp +++ b/src/hotspot/share/nmt/vmatree.cpp @@ -807,7 +807,7 @@ void VMATree::SummaryDiff::grow_and_rehash() { // Clear previous -- if applicable if (_members != _small) { - FREE_C_HEAP_ARRAY(KVEntry, _members); + FREE_C_HEAP_ARRAY(_members); } _members = NEW_C_HEAP_ARRAY(KVEntry, new_len, mtNMT); @@ -847,7 +847,7 @@ void VMATree::SummaryDiff::add(const SummaryDiff& other) { void VMATree::SummaryDiff::clear() { if (_members != _small) { - FREE_C_HEAP_ARRAY(KVEntry, _members); + FREE_C_HEAP_ARRAY(_members); } memset(_small, 0, sizeof(_small)); } diff --git a/src/hotspot/share/nmt/vmatree.hpp b/src/hotspot/share/nmt/vmatree.hpp index ae732a4b0d3..7054249319f 100644 --- a/src/hotspot/share/nmt/vmatree.hpp +++ b/src/hotspot/share/nmt/vmatree.hpp @@ -264,7 +264,7 @@ public: } ~SummaryDiff() { if (_members != _small) { - FREE_C_HEAP_ARRAY(KVEntry, _members); + FREE_C_HEAP_ARRAY(_members); } } diff --git a/src/hotspot/share/oops/accessDecorators.hpp b/src/hotspot/share/oops/accessDecorators.hpp index b972c54a064..bbf5cd8722e 100644 --- a/src/hotspot/share/oops/accessDecorators.hpp +++ b/src/hotspot/share/oops/accessDecorators.hpp @@ -108,7 +108,8 @@ const DecoratorSet INTERNAL_DECORATOR_MASK = INTERNAL_CONVERT_COMPRESS // - Guarantees from relaxed loads hold. // * MO_SEQ_CST: Sequentially consistent loads. // - These loads observe MO_SEQ_CST stores in the same order on other processors -// - Preceding loads and stores in program order are not reordered with subsequent loads and stores in program order. +// - Preceding MO_SEQ_CST loads and stores in program order are not reordered with +// subsequent MO_SEQ_CST loads and stores in program order. // - Guarantees from acquiring loads hold. // === Atomic Cmpxchg === // * MO_RELAXED: Atomic but relaxed cmpxchg. diff --git a/src/hotspot/share/oops/arrayOop.hpp b/src/hotspot/share/oops/arrayOop.hpp index f0c476a2486..836a1b9250d 100644 --- a/src/hotspot/share/oops/arrayOop.hpp +++ b/src/hotspot/share/oops/arrayOop.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -80,8 +80,7 @@ private: // The _length field is not declared in C++. It is allocated after the // mark-word when using compact headers (+UseCompactObjectHeaders), otherwise - // after the compressed Klass* when running with compressed class-pointers - // (+UseCompressedClassPointers), or else after the full Klass*. + // after the compressed Klass*. static int length_offset_in_bytes() { return oopDesc::base_offset_in_bytes(); } diff --git a/src/hotspot/share/oops/compressedKlass.cpp b/src/hotspot/share/oops/compressedKlass.cpp index b32d10c74d2..ca1c46d4095 100644 --- a/src/hotspot/share/oops/compressedKlass.cpp +++ b/src/hotspot/share/oops/compressedKlass.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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 @@ -95,7 +95,7 @@ void CompressedKlassPointers::sanity_check_after_initialization() { // We should need a class space if address space is larger than what narrowKlass can address const bool should_need_class_space = (BytesPerWord * BitsPerByte) > narrow_klass_pointer_bits(); - ASSERT_HERE(should_need_class_space == needs_class_space()); + ASSERT_HERE(should_need_class_space == (INCLUDE_CLASS_SPACE ? true : false)); const size_t klass_align = klass_alignment_in_bytes(); @@ -318,24 +318,19 @@ void CompressedKlassPointers::initialize(address addr, size_t len) { } void CompressedKlassPointers::print_mode(outputStream* st) { - st->print_cr("UseCompressedClassPointers %d, UseCompactObjectHeaders %d", - UseCompressedClassPointers, UseCompactObjectHeaders); - if (UseCompressedClassPointers) { - st->print_cr("Narrow klass pointer bits %d, Max shift %d", - _narrow_klass_pointer_bits, _max_shift); - st->print_cr("Narrow klass base: " PTR_FORMAT ", Narrow klass shift: %d", - p2i(base()), shift()); - st->print_cr("Encoding Range: " RANGE2FMT, RANGE2FMTARGS(_base, encoding_range_end())); - st->print_cr("Klass Range: " RANGE2FMT, RANGE2FMTARGS(_klass_range_start, _klass_range_end)); - st->print_cr("Klass ID Range: [%u - %u) (%u)", _lowest_valid_narrow_klass_id, _highest_valid_narrow_klass_id + 1, - _highest_valid_narrow_klass_id + 1 - _lowest_valid_narrow_klass_id); - if (_protection_zone_size > 0) { - st->print_cr("Protection zone: " RANGEFMT, RANGEFMTARGS(_base, _protection_zone_size)); - } else { - st->print_cr("No protection zone."); - } + st->print_cr("UseCompactObjectHeaders %d", UseCompactObjectHeaders); + st->print_cr("Narrow klass pointer bits %d, Max shift %d", + _narrow_klass_pointer_bits, _max_shift); + st->print_cr("Narrow klass base: " PTR_FORMAT ", Narrow klass shift: %d", + p2i(base()), shift()); + st->print_cr("Encoding Range: " RANGE2FMT, RANGE2FMTARGS(_base, encoding_range_end())); + st->print_cr("Klass Range: " RANGE2FMT, RANGE2FMTARGS(_klass_range_start, _klass_range_end)); + st->print_cr("Klass ID Range: [%u - %u) (%u)", _lowest_valid_narrow_klass_id, _highest_valid_narrow_klass_id + 1, + _highest_valid_narrow_klass_id + 1 - _lowest_valid_narrow_klass_id); + if (_protection_zone_size > 0) { + st->print_cr("Protection zone: " RANGEFMT, RANGEFMTARGS(_base, _protection_zone_size)); } else { - st->print_cr("UseCompressedClassPointers off"); + st->print_cr("No protection zone."); } } diff --git a/src/hotspot/share/oops/compressedKlass.hpp b/src/hotspot/share/oops/compressedKlass.hpp index 98a9eafdbf4..fe1ce9e07ae 100644 --- a/src/hotspot/share/oops/compressedKlass.hpp +++ b/src/hotspot/share/oops/compressedKlass.hpp @@ -98,7 +98,6 @@ class Klass; // If compressed klass pointers then use narrowKlass. typedef juint narrowKlass; -// For UseCompressedClassPointers. class CompressedKlassPointers : public AllStatic { friend class VMStructs; friend class ArchiveBuilder; @@ -161,7 +160,6 @@ public: // Initialization sequence: // 1) Parse arguments. The following arguments take a role: - // - UseCompressedClassPointers // - UseCompactObjectHeaders // - Xshare on off dump // - CompressedClassSpaceSize @@ -192,12 +190,6 @@ public: // resulting from the current encoding settings (base, shift), capped to a certain max. value. static size_t max_klass_range_size(); - // On 64-bit, we need the class space to confine Klass structures to the encoding range, which is determined - // by bit size of narrowKlass IDs and the shift. On 32-bit, we support compressed class pointer only - // "pro-forma": narrowKlass have the same size as addresses (32 bits), and therefore the encoding range is - // equal to the address space size. Here, we don't need a class space. - static constexpr bool needs_class_space() { return LP64_ONLY(true) NOT_LP64(false); } - // Reserve a range of memory that is to contain Klass strucutures which are referenced by narrow Klass IDs. // If optimize_for_zero_base is true, the implementation will attempt to reserve optimized for zero-based encoding. static char* reserve_address_space_for_compressed_classes(size_t size, bool aslr, bool optimize_for_zero_base); diff --git a/src/hotspot/share/oops/compressedKlass.inline.hpp b/src/hotspot/share/oops/compressedKlass.inline.hpp index 65732b3b289..834264286bc 100644 --- a/src/hotspot/share/oops/compressedKlass.inline.hpp +++ b/src/hotspot/share/oops/compressedKlass.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, 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 @@ -75,7 +75,6 @@ inline narrowKlass CompressedKlassPointers::encode(const Klass* v) { #ifdef ASSERT inline void CompressedKlassPointers::check_encodable(const void* addr) { - assert(UseCompressedClassPointers, "Only call for +UseCCP"); assert(addr != nullptr, "Null Klass?"); assert(is_encodable(addr), "Address " PTR_FORMAT " is not encodable (Klass range: " RANGEFMT ", klass alignment: %d)", @@ -84,7 +83,6 @@ inline void CompressedKlassPointers::check_encodable(const void* addr) { inline void CompressedKlassPointers::check_valid_narrow_klass_id(narrowKlass nk) { check_init(_base); - assert(UseCompressedClassPointers, "Only call for +UseCCP"); assert(nk > 0, "narrow Klass ID is 0"); const uint64_t nk_mask = ~right_n_bits(narrow_klass_pointer_bits()); assert(((uint64_t)nk & nk_mask) == 0, "narrow klass id bit spillover (%u)", nk); diff --git a/src/hotspot/share/oops/generateOopMap.cpp b/src/hotspot/share/oops/generateOopMap.cpp index 09912aeaf63..56f3e7e0325 100644 --- a/src/hotspot/share/oops/generateOopMap.cpp +++ b/src/hotspot/share/oops/generateOopMap.cpp @@ -1168,42 +1168,46 @@ void GenerateOopMap::interp_bb(BasicBlock *bb) { } void GenerateOopMap::do_exception_edge(BytecodeStream* itr) { - // Only check exception edge, if bytecode can trap - if (!Bytecodes::can_trap(itr->code())) return; - switch (itr->code()) { - case Bytecodes::_aload_0: - // These bytecodes can trap for rewriting. We need to assume that - // they do not throw exceptions to make the monitor analysis work. - return; - case Bytecodes::_ireturn: - case Bytecodes::_lreturn: - case Bytecodes::_freturn: - case Bytecodes::_dreturn: - case Bytecodes::_areturn: - case Bytecodes::_return: - // If the monitor stack height is not zero when we leave the method, - // then we are either exiting with a non-empty stack or we have - // found monitor trouble earlier in our analysis. In either case, - // assume an exception could be taken here. - if (_monitor_top == 0) { + // Only check exception edge, if bytecode can trap or if async exceptions can be thrown + // from any bytecode in the interpreter when single stepping. + if (!_all_exception_edges) { + if (!Bytecodes::can_trap(itr->code())) return; + switch (itr->code()) { + case Bytecodes::_aload_0: + // These bytecodes can trap for rewriting. We need to assume that + // they do not throw exceptions to make the monitor analysis work. return; - } - break; - case Bytecodes::_monitorexit: - // If the monitor stack height is bad_monitors, then we have detected a - // monitor matching problem earlier in the analysis. If the - // monitor stack height is 0, we are about to pop a monitor - // off of an empty stack. In either case, the bytecode - // could throw an exception. - if (_monitor_top != bad_monitors && _monitor_top != 0) { - return; - } - break; + case Bytecodes::_ireturn: + case Bytecodes::_lreturn: + case Bytecodes::_freturn: + case Bytecodes::_dreturn: + case Bytecodes::_areturn: + case Bytecodes::_return: + // If the monitor stack height is not zero when we leave the method, + // then we are either exiting with a non-empty stack or we have + // found monitor trouble earlier in our analysis. In either case, + // assume an exception could be taken here. + if (_monitor_top == 0) { + return; + } + break; - default: - break; + case Bytecodes::_monitorexit: + // If the monitor stack height is bad_monitors, then we have detected a + // monitor matching problem earlier in the analysis. If the + // monitor stack height is 0, we are about to pop a monitor + // off of an empty stack. In either case, the bytecode + // could throw an exception. + if (_monitor_top != bad_monitors && _monitor_top != 0) { + return; + } + break; + + default: + break; + } } if (_has_exceptions) { @@ -2055,12 +2059,12 @@ void GenerateOopMap::print_time() { // // ============ Main Entry Point =========== // -GenerateOopMap::GenerateOopMap(const methodHandle& method) { +GenerateOopMap::GenerateOopMap(const methodHandle& method, bool all_exception_edges) : // We have to initialize all variables here, that can be queried directly - _method = method; - _max_locals=0; - _init_vars = nullptr; -} + _method(method), + _max_locals(0), + _all_exception_edges(all_exception_edges), + _init_vars(nullptr) {} bool GenerateOopMap::compute_map(Thread* current) { #ifndef PRODUCT @@ -2187,7 +2191,7 @@ void GenerateOopMap::result_for_basicblock(int bci) { // Find basicblock and report results BasicBlock* bb = get_basic_block_containing(bci); guarantee(bb != nullptr, "no basic block for bci"); - assert(bb->is_reachable(), "getting result from unreachable basicblock %d", bci); + assert(bb->is_reachable(), "getting result from unreachable basicblock at bci %d", bci); bb->set_changed(true); interp_bb(bb); } diff --git a/src/hotspot/share/oops/generateOopMap.hpp b/src/hotspot/share/oops/generateOopMap.hpp index 783e295f08a..f0fdfeda57f 100644 --- a/src/hotspot/share/oops/generateOopMap.hpp +++ b/src/hotspot/share/oops/generateOopMap.hpp @@ -307,6 +307,7 @@ class GenerateOopMap { bool _did_relocation; // was relocation necessary bool _monitor_safe; // The monitors in this method have been determined // to be safe. + bool _all_exception_edges; // All bytecodes can reach containing exception handler. // Working Cell type state int _state_len; // Size of states @@ -455,7 +456,7 @@ class GenerateOopMap { friend class RelocCallback; public: - GenerateOopMap(const methodHandle& method); + GenerateOopMap(const methodHandle& method, bool all_exception_edges); // Compute the map - returns true on success and false on error. bool compute_map(Thread* current); @@ -516,7 +517,7 @@ class ResolveOopMapConflicts: public GenerateOopMap { #endif public: - ResolveOopMapConflicts(const methodHandle& method) : GenerateOopMap(method) { } + ResolveOopMapConflicts(const methodHandle& method) : GenerateOopMap(method, true) { } methodHandle do_potential_rewrite(TRAPS); }; @@ -535,7 +536,7 @@ class GeneratePairingInfo: public GenerateOopMap { CellTypeState* stack, int stack_top) {} public: - GeneratePairingInfo(const methodHandle& method) : GenerateOopMap(method) {}; + GeneratePairingInfo(const methodHandle& method) : GenerateOopMap(method, false) {}; // Call compute_map() to generate info. }; diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp index 1963327fc78..9f33e6351a9 100644 --- a/src/hotspot/share/oops/instanceKlass.cpp +++ b/src/hotspot/share/oops/instanceKlass.cpp @@ -23,6 +23,7 @@ */ #include "cds/aotClassInitializer.hpp" +#include "cds/aotLinkedClassBulkLoader.hpp" #include "cds/aotMetaspace.hpp" #include "cds/archiveUtils.hpp" #include "cds/cdsConfig.hpp" @@ -150,6 +151,7 @@ #endif // ndef DTRACE_ENABLED bool InstanceKlass::_finalization_enabled = true; +static int call_class_initializer_counter = 0; // for debugging static inline bool is_class_loader(const Symbol* class_name, const ClassFileParser& parser) { @@ -484,10 +486,8 @@ InstanceKlass* InstanceKlass::allocate_instance_klass(const ClassFileParser& par ik = new (loader_data, size, THREAD) InstanceKlass(parser); } - if (ik != nullptr && UseCompressedClassPointers) { - assert(CompressedKlassPointers::is_encodable(ik), - "Klass " PTR_FORMAT "needs a narrow Klass ID, but is not encodable", p2i(ik)); - } + assert(ik == nullptr || CompressedKlassPointers::is_encodable(ik), + "Klass " PTR_FORMAT "needs a narrow Klass ID, but is not encodable", p2i(ik)); // Check for pending exception before adding to the loader data and incrementing // class count. Can get OOM here. @@ -704,6 +704,7 @@ void InstanceKlass::deallocate_contents(ClassLoaderData* loader_data) { if (constants() != nullptr) { assert (!constants()->on_stack(), "shouldn't be called if anything is onstack"); if (!constants()->in_aot_cache()) { + HeapShared::remove_scratch_resolved_references(constants()); MetadataFactory::free_metadata(loader_data, constants()); } // Delete any cached resolution errors for the constant pool @@ -884,7 +885,9 @@ void InstanceKlass::assert_no_clinit_will_run_for_aot_initialized_class() const #endif #if INCLUDE_CDS -void InstanceKlass::initialize_with_aot_initialized_mirror(TRAPS) { +// early_init -- we are moving this class into the fully_initialized state before the +// JVM is able to execute any bytecodes. See AOTLinkedClassBulkLoader::is_initializing_classes_early(). +void InstanceKlass::initialize_with_aot_initialized_mirror(bool early_init, TRAPS) { assert(has_aot_initialized_mirror(), "must be"); assert(CDSConfig::is_loading_heap(), "must be"); assert(CDSConfig::is_using_aot_linked_classes(), "must be"); @@ -894,15 +897,36 @@ void InstanceKlass::initialize_with_aot_initialized_mirror(TRAPS) { return; } + if (log_is_enabled(Info, aot, init)) { + ResourceMark rm; + log_info(aot, init)("%s (aot-inited%s)", external_name(), early_init ? ", early" : ""); + } + if (is_runtime_setup_required()) { + assert(!early_init, "must not call"); // Need to take the slow path, which will call the runtimeSetup() function instead // of initialize(CHECK); return; } - if (log_is_enabled(Info, aot, init)) { - ResourceMark rm; - log_info(aot, init)("%s (aot-inited)", external_name()); + + LogTarget(Info, class, init) lt; + if (lt.is_enabled()) { + ResourceMark rm(THREAD); + LogStream ls(lt); + ls.print("%d Initializing ", call_class_initializer_counter++); + name()->print_value_on(&ls); + ls.print_cr("(aot-inited) (" PTR_FORMAT ") by thread \"%s\"", + p2i(this), THREAD->name()); + } + + if (early_init) { + precond(AOTLinkedClassBulkLoader::is_initializing_classes_early()); + precond(is_linked()); + precond(init_thread() == nullptr); + set_init_state(fully_initialized); + fence_and_clear_init_lock(); + return; } link_class(CHECK); @@ -1098,6 +1122,12 @@ bool InstanceKlass::link_class_impl(TRAPS) { } } } + + if (log_is_enabled(Info, class, link)) { + ResourceMark rm(THREAD); + log_info(class, link)("Linked class %s", external_name()); + } + return true; } @@ -1699,8 +1729,6 @@ ArrayKlass* InstanceKlass::array_klass_or_null() { return array_klass_or_null(1); } -static int call_class_initializer_counter = 0; // for debugging - Method* InstanceKlass::class_initializer() const { Method* clinit = find_method( vmSymbols::class_initializer_name(), vmSymbols::void_method_signature()); @@ -2514,7 +2542,7 @@ void InstanceKlass::update_methods_jmethod_cache() { new_cache[i] = cache[i]; } _methods_jmethod_ids = new_cache; - FREE_C_HEAP_ARRAY(jmethodID, cache); + FREE_C_HEAP_ARRAY(cache); } } } @@ -2988,7 +3016,7 @@ void InstanceKlass::release_C_heap_structures(bool release_sub_metadata) { } #endif - FREE_C_HEAP_ARRAY(char, _source_debug_extension); + FREE_C_HEAP_ARRAY(_source_debug_extension); if (release_sub_metadata) { constants()->release_C_heap_structures(); diff --git a/src/hotspot/share/oops/instanceKlass.hpp b/src/hotspot/share/oops/instanceKlass.hpp index e370a3b7a7c..6c880811024 100644 --- a/src/hotspot/share/oops/instanceKlass.hpp +++ b/src/hotspot/share/oops/instanceKlass.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -330,9 +330,6 @@ class InstanceKlass: public Klass { bool defined_by_other_loaders() const { return _misc_flags.defined_by_other_loaders(); } void set_class_loader_type() { _misc_flags.set_class_loader_type(_class_loader_data); } - // Check if the class can be shared in CDS - bool is_shareable() const; - bool shared_loading_failed() const { return _misc_flags.shared_loading_failed(); } void set_shared_loading_failed() { _misc_flags.set_shared_loading_failed(true); } @@ -556,7 +553,7 @@ public: // initialization (virtuals from Klass) bool should_be_initialized() const override; // means that initialize should be called - void initialize_with_aot_initialized_mirror(TRAPS); + void initialize_with_aot_initialized_mirror(bool early_init, TRAPS); void assert_no_clinit_will_run_for_aot_initialized_class() const NOT_DEBUG_RETURN; void initialize(TRAPS) override; void initialize_preemptable(TRAPS) override; @@ -1136,8 +1133,6 @@ private: void link_previous_versions(InstanceKlass* pv) { _previous_versions = pv; } void mark_newly_obsolete_methods(Array* old_methods, int emcp_method_count); #endif - // log class name to classlist - void log_to_classlist() const; public: #if INCLUDE_CDS diff --git a/src/hotspot/share/oops/klass.cpp b/src/hotspot/share/oops/klass.cpp index 001e9eba790..84a1766a702 100644 --- a/src/hotspot/share/oops/klass.cpp +++ b/src/hotspot/share/oops/klass.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -1055,14 +1055,8 @@ void Klass::verify_on(outputStream* st) { // This can be expensive, but it is worth checking that this klass is actually // in the CLD graph but not in production. -#ifdef ASSERT - if (UseCompressedClassPointers) { - // Stricter checks for both correct alignment and placement - CompressedKlassPointers::check_encodable(this); - } else { - assert(Metaspace::contains((address)this), "Should be"); - } -#endif // ASSERT + // Stricter checks for both correct alignment and placement + DEBUG_ONLY(CompressedKlassPointers::check_encodable(this)); guarantee(this->is_klass(),"should be klass"); diff --git a/src/hotspot/share/oops/klass.hpp b/src/hotspot/share/oops/klass.hpp index d59db9744cb..1c6b28127b8 100644 --- a/src/hotspot/share/oops/klass.hpp +++ b/src/hotspot/share/oops/klass.hpp @@ -466,9 +466,9 @@ protected: static const int _lh_log2_element_size_shift = BitsPerByte*0; static const int _lh_log2_element_size_mask = BitsPerLong-1; static const int _lh_element_type_shift = BitsPerByte*1; - static const int _lh_element_type_mask = right_n_bits(BitsPerByte); // shifted mask + static const int _lh_element_type_mask = right_n_bits(BitsPerByte); // shifted mask static const int _lh_header_size_shift = BitsPerByte*2; - static const int _lh_header_size_mask = right_n_bits(BitsPerByte); // shifted mask + static const int _lh_header_size_mask = right_n_bits(BitsPerByte); // shifted mask static const int _lh_array_tag_bits = 2; static const int _lh_array_tag_shift = BitsPerInt - _lh_array_tag_bits; static const int _lh_array_tag_obj_value = ~0x01; // 0x80000000 >> 30 diff --git a/src/hotspot/share/oops/markWord.hpp b/src/hotspot/share/oops/markWord.hpp index c54a9f1bf5d..4583e6bd3a1 100644 --- a/src/hotspot/share/oops/markWord.hpp +++ b/src/hotspot/share/oops/markWord.hpp @@ -54,7 +54,6 @@ // // - the two lock bits are used to describe three states: locked/unlocked and monitor. // -// [ptr | 00] locked ptr points to real header on stack (stack-locking in use) // [header | 00] locked locked regular object header (fast-locking in use) // [header | 01] unlocked regular object header // [ptr | 10] monitor inflated lock (header is swapped out, UseObjectMonitorTable == false) diff --git a/src/hotspot/share/oops/methodData.cpp b/src/hotspot/share/oops/methodData.cpp index 38bdc33c628..dc0b8fa9f81 100644 --- a/src/hotspot/share/oops/methodData.cpp +++ b/src/hotspot/share/oops/methodData.cpp @@ -329,7 +329,7 @@ static bool is_excluded(Klass* k) { log_debug(aot, training)("Purged %s from MDO: unloaded class", k->name()->as_C_string()); return true; } else { - bool excluded = SystemDictionaryShared::should_be_excluded(k); + bool excluded = SystemDictionaryShared::should_be_excluded(k) || !SystemDictionaryShared::is_builtin_loader(k->class_loader_data()); if (excluded) { log_debug(aot, training)("Purged %s from MDO: excluded class", k->name()->as_C_string()); } @@ -667,8 +667,8 @@ void MultiBranchData::print_data_on(outputStream* st, const char* extra) const { void ArgInfoData::print_data_on(outputStream* st, const char* extra) const { print_shared(st, "ArgInfoData", extra); - int nargs = number_of_args(); - for (int i = 0; i < nargs; i++) { + int args_size = size_of_args(); + for (int i = 0; i < args_size; i++) { st->print(" 0x%x", arg_modified(i)); } st->cr(); diff --git a/src/hotspot/share/oops/methodData.hpp b/src/hotspot/share/oops/methodData.hpp index 196537359b5..45529618afb 100644 --- a/src/hotspot/share/oops/methodData.hpp +++ b/src/hotspot/share/oops/methodData.hpp @@ -1751,7 +1751,7 @@ public: virtual bool is_ArgInfoData() const { return true; } - int number_of_args() const { + int size_of_args() const { return array_len(); } diff --git a/src/hotspot/share/oops/methodData.inline.hpp b/src/hotspot/share/oops/methodData.inline.hpp index dee14d49253..b417ba867fc 100644 --- a/src/hotspot/share/oops/methodData.inline.hpp +++ b/src/hotspot/share/oops/methodData.inline.hpp @@ -59,7 +59,7 @@ inline uint MethodData::arg_modified(int a) { MutexLocker ml(extra_data_lock(), Mutex::_no_safepoint_check_flag); ArgInfoData* aid = arg_info(); assert(aid != nullptr, "arg_info must be not null"); - assert(a >= 0 && a < aid->number_of_args(), "valid argument number"); + assert(a >= 0 && a < aid->size_of_args(), "valid argument number"); return aid->arg_modified(a); } @@ -68,7 +68,7 @@ inline void MethodData::set_arg_modified(int a, uint v) { MutexLocker ml(extra_data_lock(), Mutex::_no_safepoint_check_flag); ArgInfoData* aid = arg_info(); assert(aid != nullptr, "arg_info must be not null"); - assert(a >= 0 && a < aid->number_of_args(), "valid argument number"); + assert(a >= 0 && a < aid->size_of_args(), "valid argument number"); aid->set_arg_modified(a, v); } diff --git a/src/hotspot/share/oops/objLayout.cpp b/src/hotspot/share/oops/objLayout.cpp index b8cd8249da1..2c426a7ddff 100644 --- a/src/hotspot/share/oops/objLayout.cpp +++ b/src/hotspot/share/oops/objLayout.cpp @@ -38,21 +38,17 @@ void ObjLayout::initialize() { _klass_mode = Compact; _oop_base_offset_in_bytes = sizeof(markWord); _oop_has_klass_gap = false; - } else if (UseCompressedClassPointers) { + } else { _klass_mode = Compressed; _oop_base_offset_in_bytes = sizeof(markWord) + sizeof(narrowKlass); _oop_has_klass_gap = true; - } else { - _klass_mode = Uncompressed; - _oop_base_offset_in_bytes = sizeof(markWord) + sizeof(Klass*); - _oop_has_klass_gap = false; } #else assert(_klass_mode == Undefined, "ObjLayout initialized twice"); assert(!UseCompactObjectHeaders, "COH unsupported on 32-bit"); - // We support +-UseCompressedClassPointers on 32-bit, but the layout + // We support narrow Klass pointers on 32-bit, but the layout // is exactly the same as it was with uncompressed klass pointers - _klass_mode = UseCompressedClassPointers ? Compressed : Uncompressed; + _klass_mode = Compressed; _oop_base_offset_in_bytes = sizeof(markWord) + sizeof(Klass*); _oop_has_klass_gap = false; #endif diff --git a/src/hotspot/share/oops/objLayout.hpp b/src/hotspot/share/oops/objLayout.hpp index e434524d4b0..37ed0b7a532 100644 --- a/src/hotspot/share/oops/objLayout.hpp +++ b/src/hotspot/share/oops/objLayout.hpp @@ -27,8 +27,8 @@ /* * This class helps to avoid loading more than one flag in some - * operations that require checking UseCompressedClassPointers, - * UseCompactObjectHeaders and possibly more. + * operations that require checking UseCompactObjectHeaders and - in the future - + * possibly more. * * This is important on some performance critical paths, e.g. where * the Klass* is accessed frequently, especially by GC oop iterators @@ -37,12 +37,10 @@ class ObjLayout { public: enum Mode { - // +UseCompactObjectHeaders (implies +UseCompressedClassPointers) + // +UseCompactObjectHeaders Compact, - // +UseCompressedClassPointers (-UseCompactObjectHeaders) + // -UseCompactObjectHeaders (compressed Klass pointers) Compressed, - // -UseCompressedClassPointers (-UseCompactObjectHeaders) - Uncompressed, // Not yet initialized Undefined }; diff --git a/src/hotspot/share/oops/objLayout.inline.hpp b/src/hotspot/share/oops/objLayout.inline.hpp index 6aa9e39ce28..adad490378d 100644 --- a/src/hotspot/share/oops/objLayout.inline.hpp +++ b/src/hotspot/share/oops/objLayout.inline.hpp @@ -32,10 +32,8 @@ inline ObjLayout::Mode ObjLayout::klass_mode() { assert(_klass_mode != Undefined, "KlassMode not yet initialized"); if (UseCompactObjectHeaders) { assert(_klass_mode == Compact, "Klass mode does not match flags"); - } else if (UseCompressedClassPointers) { - assert(_klass_mode == Compressed, "Klass mode does not match flags"); } else { - assert(_klass_mode == Uncompressed, "Klass mode does not match flags"); + assert(_klass_mode == Compressed, "Klass mode does not match flags"); } #endif return _klass_mode; diff --git a/src/hotspot/share/oops/oop.cpp b/src/hotspot/share/oops/oop.cpp index 5f453241c3d..415732af4f6 100644 --- a/src/hotspot/share/oops/oop.cpp +++ b/src/hotspot/share/oops/oop.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -152,8 +152,7 @@ bool oopDesc::is_typeArray_noinline() const { return is_typeArray(); } #if INCLUDE_CDS_JAVA_HEAP void oopDesc::set_narrow_klass(narrowKlass nk) { assert(CDSConfig::is_dumping_heap(), "Used by CDS only. Do not abuse!"); - assert(UseCompressedClassPointers, "must be"); - _metadata._compressed_klass = nk; + _compressed_klass = nk; } #endif diff --git a/src/hotspot/share/oops/oop.hpp b/src/hotspot/share/oops/oop.hpp index 0dc6590750e..d6cc71a60d8 100644 --- a/src/hotspot/share/oops/oop.hpp +++ b/src/hotspot/share/oops/oop.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -49,10 +49,7 @@ class oopDesc { friend class JVMCIVMStructs; private: volatile markWord _mark; - union _metadata { - Klass* _klass; - narrowKlass _compressed_klass; - } _metadata; + narrowKlass _compressed_klass; // There may be ordering constraints on the initialization of fields that // make use of the C++ copy/assign incorrect. @@ -338,7 +335,7 @@ class oopDesc { } else #endif { - return (int)offset_of(oopDesc, _metadata._klass); + return (int)offset_of(oopDesc, _compressed_klass); } } static int klass_gap_offset_in_bytes() { diff --git a/src/hotspot/share/oops/oop.inline.hpp b/src/hotspot/share/oops/oop.inline.hpp index b445eae933b..d5cb80e1122 100644 --- a/src/hotspot/share/oops/oop.inline.hpp +++ b/src/hotspot/share/oops/oop.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -99,9 +99,9 @@ Klass* oopDesc::klass() const { case ObjLayout::Compact: return mark().klass(); case ObjLayout::Compressed: - return CompressedKlassPointers::decode_not_null(_metadata._compressed_klass); + return CompressedKlassPointers::decode_not_null(_compressed_klass); default: - return _metadata._klass; + ShouldNotReachHere(); } } @@ -110,9 +110,9 @@ Klass* oopDesc::klass_or_null() const { case ObjLayout::Compact: return mark().klass_or_null(); case ObjLayout::Compressed: - return CompressedKlassPointers::decode(_metadata._compressed_klass); + return CompressedKlassPointers::decode(_compressed_klass); default: - return _metadata._klass; + ShouldNotReachHere(); } } @@ -121,11 +121,11 @@ Klass* oopDesc::klass_or_null_acquire() const { case ObjLayout::Compact: return mark_acquire().klass(); case ObjLayout::Compressed: { - narrowKlass narrow_klass = AtomicAccess::load_acquire(&_metadata._compressed_klass); + narrowKlass narrow_klass = AtomicAccess::load_acquire(&_compressed_klass); return CompressedKlassPointers::decode(narrow_klass); } default: - return AtomicAccess::load_acquire(&_metadata._klass); + ShouldNotReachHere(); } } @@ -134,9 +134,9 @@ Klass* oopDesc::klass_without_asserts() const { case ObjLayout::Compact: return mark().klass_without_asserts(); case ObjLayout::Compressed: - return CompressedKlassPointers::decode_without_asserts(_metadata._compressed_klass); + return CompressedKlassPointers::decode_without_asserts(_compressed_klass); default: - return _metadata._klass; + ShouldNotReachHere(); } } @@ -145,7 +145,7 @@ narrowKlass oopDesc::narrow_klass() const { case ObjLayout::Compact: return mark().narrow_klass(); case ObjLayout::Compressed: - return _metadata._compressed_klass; + return _compressed_klass; default: ShouldNotReachHere(); } @@ -154,23 +154,14 @@ narrowKlass oopDesc::narrow_klass() const { void oopDesc::set_klass(Klass* k) { assert(Universe::is_bootstrapping() || (k != nullptr && k->is_klass()), "incorrect Klass"); assert(!UseCompactObjectHeaders, "don't set Klass* with compact headers"); - if (UseCompressedClassPointers) { - _metadata._compressed_klass = CompressedKlassPointers::encode_not_null(k); - } else { - _metadata._klass = k; - } + _compressed_klass = CompressedKlassPointers::encode_not_null(k); } void oopDesc::release_set_klass(HeapWord* mem, Klass* k) { assert(Universe::is_bootstrapping() || (k != nullptr && k->is_klass()), "incorrect Klass"); assert(!UseCompactObjectHeaders, "don't set Klass* with compact headers"); char* raw_mem = ((char*)mem + klass_offset_in_bytes()); - if (UseCompressedClassPointers) { - AtomicAccess::release_store((narrowKlass*)raw_mem, - CompressedKlassPointers::encode_not_null(k)); - } else { - AtomicAccess::release_store((Klass**)raw_mem, k); - } + AtomicAccess::release_store((narrowKlass*)raw_mem, CompressedKlassPointers::encode_not_null(k)); } void oopDesc::set_klass_gap(HeapWord* mem, int v) { diff --git a/src/hotspot/share/oops/trainingData.cpp b/src/hotspot/share/oops/trainingData.cpp index f52c22ad38a..7976da35374 100644 --- a/src/hotspot/share/oops/trainingData.cpp +++ b/src/hotspot/share/oops/trainingData.cpp @@ -118,10 +118,23 @@ void TrainingData::verify() { } } +static bool is_excluded(InstanceKlass* k) { + if (!k->is_loaded() || k->has_been_redefined()) { + return true; + } + if (CDSConfig::is_at_aot_safepoint()) { + // Check for AOT exclusion only at AOT safe point. + return SystemDictionaryShared::should_be_excluded(k) || !SystemDictionaryShared::is_builtin_loader(k->class_loader_data()); + } + return false; +} + MethodTrainingData* MethodTrainingData::make(const methodHandle& method, bool null_if_not_found, bool use_cache) { - MethodTrainingData* mtd = nullptr; if (!have_data() && !need_data()) { - return mtd; + return nullptr; + } + if (is_excluded(method->method_holder())) { + return nullptr; } // Try grabbing the cached value first. // Cache value is stored in MethodCounters and the following are the @@ -133,6 +146,7 @@ MethodTrainingData* MethodTrainingData::make(const methodHandle& method, bool nu // i.e. null_if_no_found == true, then just return a null. // 3. Cache value is not null. // Return it, the value of training_data_lookup_failed doesn't matter. + MethodTrainingData* mtd = nullptr; MethodCounters* mcs = method->method_counters(); if (mcs != nullptr) { mtd = mcs->method_training_data(); @@ -175,6 +189,7 @@ MethodTrainingData* MethodTrainingData::make(const methodHandle& method, bool nu return nullptr; // allocation failure } td = training_data_set()->install(mtd); + assert(!is_excluded(method->method_holder()), "Should not be excluded"); assert(td == mtd, ""); } else { mtd = nullptr; @@ -376,6 +391,9 @@ void CompileTrainingData::prepare(Visitor& visitor) { } KlassTrainingData* KlassTrainingData::make(InstanceKlass* holder, bool null_if_not_found) { + if (is_excluded(holder)) { + return nullptr; + } Key key(holder); TrainingData* td = CDS_ONLY(have_data() ? lookup_archived_training_data(&key) :) nullptr; KlassTrainingData* ktd = nullptr; @@ -401,6 +419,7 @@ KlassTrainingData* KlassTrainingData::make(InstanceKlass* holder, bool null_if_n } td = training_data_set()->install(ktd); assert(ktd == td, ""); + assert(!is_excluded(holder), "Should not be excluded"); } else { ktd = td->as_KlassTrainingData(); guarantee(ktd->holder() != nullptr, "null holder"); @@ -543,18 +562,24 @@ void TrainingData::cleanup_training_data() { } } +void TrainingData::cleanup_after_redefinition() { + if (need_data()) { + TrainingDataLocker l; + ResourceMark rm; + Visitor visitor(training_data_set()->size()); + training_data_set()->iterate([&](TrainingData* td) { + td->cleanup(visitor); + }); + } +} + void KlassTrainingData::cleanup(Visitor& visitor) { if (visitor.is_visited(this)) { return; } visitor.visit(this); if (has_holder()) { - bool is_excluded = !holder()->is_loaded(); - if (CDSConfig::is_at_aot_safepoint()) { - // Check for AOT exclusion only at AOT safe point. - is_excluded |= SystemDictionaryShared::should_be_excluded(holder()); - } - if (is_excluded) { + if (is_excluded(holder())) { ResourceMark rm; log_debug(aot, training)("Cleanup KTD %s", name()->as_klass_external_name()); _holder = nullptr; @@ -572,12 +597,8 @@ void MethodTrainingData::cleanup(Visitor& visitor) { } visitor.visit(this); if (has_holder()) { - if (CDSConfig::is_at_aot_safepoint() && SystemDictionaryShared::should_be_excluded(holder()->method_holder())) { - // Check for AOT exclusion only at AOT safe point. + if (is_excluded(holder()->method_holder())) { log_debug(aot, training)("Cleanup MTD %s::%s", name()->as_klass_external_name(), signature()->as_utf8()); - if (_final_profile != nullptr && _final_profile->method() != _holder) { - log_warning(aot, training)("Stale MDO for %s::%s", name()->as_klass_external_name(), signature()->as_utf8()); - } _final_profile = nullptr; _final_counters = nullptr; _holder = nullptr; @@ -593,6 +614,7 @@ void MethodTrainingData::cleanup(Visitor& visitor) { } void KlassTrainingData::verify() { + guarantee(!has_holder() || !is_excluded(holder()), "Bad holder"); for (int i = 0; i < comp_dep_count(); i++) { CompileTrainingData* ctd = comp_dep(i); if (!ctd->_init_deps.contains(this)) { @@ -604,6 +626,7 @@ void KlassTrainingData::verify() { } void MethodTrainingData::verify(bool verify_dep_counter) { + guarantee(!has_holder() || !is_excluded(holder()->method_holder()), "Bad holder"); iterate_compiles([&](CompileTrainingData* ctd) { ctd->verify(verify_dep_counter); }); diff --git a/src/hotspot/share/oops/trainingData.hpp b/src/hotspot/share/oops/trainingData.hpp index c549004e76e..a6decdce7f0 100644 --- a/src/hotspot/share/oops/trainingData.hpp +++ b/src/hotspot/share/oops/trainingData.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, 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 @@ -217,11 +217,7 @@ public: return *prior; } template - void iterate(const Function& fn) const { // lambda enabled API - iterate(const_cast(fn)); - } - template - void iterate(Function& fn) const { // lambda enabled API + void iterate(Function fn) const { // lambda enabled API return _table.iterate_all([&](const TrainingData::Key* k, TrainingData* td) { fn(td); }); } int size() const { return _table.number_of_entries(); } @@ -304,13 +300,10 @@ private: } template - static void iterate(const Function& fn) { iterate(const_cast(fn)); } - - template - static void iterate(Function& fn) { // lambda enabled API + static void iterate(Function fn) { // lambda enabled API TrainingDataLocker l; if (have_data()) { - archived_training_data_dictionary()->iterate(fn); + archived_training_data_dictionary()->iterate_all(fn); } if (need_data()) { training_data_set()->iterate(fn); @@ -431,6 +424,8 @@ private: } return nullptr; } + + static void cleanup_after_redefinition(); }; // Training data that is associated with an InstanceKlass diff --git a/src/hotspot/share/opto/arraycopynode.cpp b/src/hotspot/share/opto/arraycopynode.cpp index 82d17440c56..2f64482f55b 100644 --- a/src/hotspot/share/opto/arraycopynode.cpp +++ b/src/hotspot/share/opto/arraycopynode.cpp @@ -258,6 +258,19 @@ Node* ArrayCopyNode::try_clone_instance(PhaseGVN *phase, bool can_reshape, int c return mem; } +// We may have narrowed the type of base because this runs with PhaseIterGVN::_delay_transform true, explicitly +// update the type of the AddP so it's consistent with its base and load() picks the right memory slice. +Node* ArrayCopyNode::make_and_transform_addp(PhaseGVN* phase, Node* base, Node* offset) { + return make_and_transform_addp(phase, base, base, offset); +} + +Node* ArrayCopyNode::make_and_transform_addp(PhaseGVN* phase, Node* base, Node* ptr, Node* offset) { + assert(phase->is_IterGVN() == nullptr || phase->is_IterGVN()->delay_transform(), "helper method when delay transform is set"); + Node* addp = phase->transform(AddPNode::make_with_base(base, ptr, offset)); + phase->set_type(addp, addp->Value(phase)); + return addp; +} + bool ArrayCopyNode::prepare_array_copy(PhaseGVN *phase, bool can_reshape, Node*& adr_src, Node*& base_src, @@ -332,12 +345,11 @@ bool ArrayCopyNode::prepare_array_copy(PhaseGVN *phase, bool can_reshape, Node* dest_scale = phase->transform(new LShiftXNode(dest_offset, phase->intcon(shift))); - adr_src = phase->transform(AddPNode::make_with_base(base_src, src_scale)); - adr_dest = phase->transform(AddPNode::make_with_base(base_dest, dest_scale)); - - adr_src = phase->transform(AddPNode::make_with_base(base_src, adr_src, phase->MakeConX(header))); - adr_dest = phase->transform(AddPNode::make_with_base(base_dest, adr_dest, phase->MakeConX(header))); + adr_src = make_and_transform_addp(phase, base_src, src_scale); + adr_dest = make_and_transform_addp(phase, base_dest, dest_scale); + adr_src = make_and_transform_addp(phase, base_src, adr_src, phase->MakeConX(header)); + adr_dest = make_and_transform_addp(phase, base_dest, adr_dest, phase->MakeConX(header)); copy_type = dest_elem; } else { assert(ary_src != nullptr, "should be a clone"); @@ -355,8 +367,8 @@ bool ArrayCopyNode::prepare_array_copy(PhaseGVN *phase, bool can_reshape, return false; } - adr_src = phase->transform(AddPNode::make_with_base(base_src, src_offset)); - adr_dest = phase->transform(AddPNode::make_with_base(base_dest, dest_offset)); + adr_src = make_and_transform_addp(phase, base_src, src_offset); + adr_dest = make_and_transform_addp(phase, base_dest, dest_offset); // The address is offsetted to an aligned address where a raw copy would start. // If the clone copy is decomposed into load-stores - the address is adjusted to @@ -366,8 +378,8 @@ bool ArrayCopyNode::prepare_array_copy(PhaseGVN *phase, bool can_reshape, int diff = arrayOopDesc::base_offset_in_bytes(elem) - offset; assert(diff >= 0, "clone should not start after 1st array element"); if (diff > 0) { - adr_src = phase->transform(AddPNode::make_with_base(base_src, adr_src, phase->MakeConX(diff))); - adr_dest = phase->transform(AddPNode::make_with_base(base_dest, adr_dest, phase->MakeConX(diff))); + adr_src = make_and_transform_addp(phase, base_src, adr_src, phase->MakeConX(diff)); + adr_dest = make_and_transform_addp(phase, base_dest, adr_dest, phase->MakeConX(diff)); } copy_type = elem; value_type = ary_src->elem(); @@ -383,6 +395,10 @@ const TypePtr* ArrayCopyNode::get_address_type(PhaseGVN* phase, const TypePtr* a return atp->add_offset(Type::OffsetBot); } +const TypePtr* ArrayCopyNode::get_src_adr_type(PhaseGVN* phase) const { + return get_address_type(phase, _src_type, in(Src)); +} + void ArrayCopyNode::array_copy_test_overlap(PhaseGVN *phase, bool can_reshape, bool disjoint_bases, int count, Node*& forward_ctl, Node*& backward_ctl) { Node* ctl = in(TypeFunc::Control); if (!disjoint_bases && count > 1) { @@ -425,12 +441,8 @@ Node* ArrayCopyNode::array_copy_forward(PhaseGVN *phase, store(bs, phase, forward_ctl, mm, adr_dest, atp_dest, v, value_type, copy_type); for (int i = 1; i < count; i++) { Node* off = phase->MakeConX(type2aelembytes(copy_type) * i); - Node* next_src = phase->transform(AddPNode::make_with_base(base_src,adr_src,off)); - // We may have narrowed the type of next_src right before calling this method but because this runs with - // PhaseIterGVN::_delay_transform true, explicitly update the type of the AddP so it's consistent with its - // base and load() picks the right memory slice. - phase->set_type(next_src, next_src->Value(phase)); - Node* next_dest = phase->transform(AddPNode::make_with_base(base_dest,adr_dest,off)); + Node* next_src = make_and_transform_addp(phase, base_src,adr_src,off); + Node* next_dest = make_and_transform_addp(phase, base_dest,adr_dest,off); // Same as above phase->set_type(next_dest, next_dest->Value(phase)); v = load(bs, phase, forward_ctl, mm, next_src, atp_src, value_type, copy_type); @@ -469,13 +481,8 @@ Node* ArrayCopyNode::array_copy_backward(PhaseGVN *phase, if (count > 0) { for (int i = count-1; i >= 1; i--) { Node* off = phase->MakeConX(type2aelembytes(copy_type) * i); - Node* next_src = phase->transform(AddPNode::make_with_base(base_src,adr_src,off)); - // We may have narrowed the type of next_src right before calling this method but because this runs with - // PhaseIterGVN::_delay_transform true, explicitly update the type of the AddP so it's consistent with its - // base and store() picks the right memory slice. - phase->set_type(next_src, next_src->Value(phase)); - Node* next_dest = phase->transform(AddPNode::make_with_base(base_dest,adr_dest,off)); - phase->set_type(next_dest, next_dest->Value(phase)); + Node* next_src = make_and_transform_addp(phase, base_src,adr_src,off); + Node* next_dest = make_and_transform_addp(phase, base_dest,adr_dest,off); Node* v = load(bs, phase, backward_ctl, mm, next_src, atp_src, value_type, copy_type); store(bs, phase, backward_ctl, mm, next_dest, atp_dest, v, value_type, copy_type); } @@ -614,21 +621,31 @@ Node *ArrayCopyNode::Ideal(PhaseGVN *phase, bool can_reshape) { phase->set_type(src, phase->type(src)->join_speculative(atp_src)); phase->set_type(dest, phase->type(dest)->join_speculative(atp_dest)); + // Control flow is going to be created, it's easier to do with _delay_transform set to true. + + // prepare_array_copy() doesn't build control flow, but it creates AddP nodes. The src/dest type possibly gets + // narrowed above. If a newly created AddP node is commoned with a pre-existing one, then the type narrowing is lost. + // Setting _delay_transform before prepare_array_copy() guarantees this doesn't happen. + if (can_reshape) { + assert(!phase->is_IterGVN()->delay_transform(), "cannot delay transforms"); + phase->is_IterGVN()->set_delay_transform(true); + } + if (!prepare_array_copy(phase, can_reshape, adr_src, base_src, adr_dest, base_dest, copy_type, value_type, disjoint_bases)) { assert(adr_src == nullptr, "no node can be left behind"); assert(adr_dest == nullptr, "no node can be left behind"); + if (can_reshape) { + assert(phase->is_IterGVN()->delay_transform(), "cannot delay transforms"); + phase->is_IterGVN()->set_delay_transform(false); + } + return nullptr; } Node* in_mem = in(TypeFunc::Memory); - if (can_reshape) { - assert(!phase->is_IterGVN()->delay_transform(), "cannot delay transforms"); - phase->is_IterGVN()->set_delay_transform(true); - } - Node* backward_ctl = phase->C->top(); Node* forward_ctl = phase->C->top(); array_copy_test_overlap(phase, can_reshape, disjoint_bases, count, forward_ctl, backward_ctl); @@ -688,7 +705,7 @@ Node *ArrayCopyNode::Ideal(PhaseGVN *phase, bool can_reshape) { return mem; } -bool ArrayCopyNode::may_modify(const TypeOopPtr* t_oop, PhaseValues* phase) { +bool ArrayCopyNode::may_modify(const TypeOopPtr* t_oop, PhaseValues* phase) const { Node* dest = in(ArrayCopyNode::Dest); if (dest->is_top()) { return false; diff --git a/src/hotspot/share/opto/arraycopynode.hpp b/src/hotspot/share/opto/arraycopynode.hpp index 83c085fd5db..aa62ee05cd0 100644 --- a/src/hotspot/share/opto/arraycopynode.hpp +++ b/src/hotspot/share/opto/arraycopynode.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, 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 @@ -95,6 +95,7 @@ public: _arraycopy_type_Type = TypeFunc::make(domain, range); } + const TypePtr* get_src_adr_type(PhaseGVN* phase) const; private: ArrayCopyNode(Compile* C, bool alloc_tightly_coupled, bool has_negative_length_guard); @@ -103,6 +104,10 @@ private: static const TypePtr* get_address_type(PhaseGVN* phase, const TypePtr* atp, Node* n); Node* try_clone_instance(PhaseGVN *phase, bool can_reshape, int count); + + Node* make_and_transform_addp(PhaseGVN* phase, Node* base, Node* offset); + Node* make_and_transform_addp(PhaseGVN* phase, Node* base, Node* ptr, Node* offset); + bool prepare_array_copy(PhaseGVN *phase, bool can_reshape, Node*& adr_src, Node*& base_src, Node*& adr_dest, Node*& base_dest, BasicType& copy_type, const Type*& value_type, bool& disjoint_bases); @@ -183,7 +188,7 @@ public: virtual bool guaranteed_safepoint() { return false; } virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual bool may_modify(const TypeOopPtr* t_oop, PhaseValues* phase); + virtual bool may_modify(const TypeOopPtr* t_oop, PhaseValues* phase) const; bool is_alloc_tightly_coupled() const { return _alloc_tightly_coupled; } diff --git a/src/hotspot/share/opto/block.cpp b/src/hotspot/share/opto/block.cpp index 7d3d4ec16f4..1da63ce8180 100644 --- a/src/hotspot/share/opto/block.cpp +++ b/src/hotspot/share/opto/block.cpp @@ -179,9 +179,11 @@ int Block::is_Empty() const { // Ideal nodes (except BoxLock) are allowable in empty blocks: skip them. Only // Mach and BoxLock nodes turn directly into code via emit(). + // Keep ReachabilityFence for diagnostic purposes. while ((end_idx > 0) && !get_node(end_idx)->is_Mach() && - !get_node(end_idx)->is_BoxLock()) { + !get_node(end_idx)->is_BoxLock() && + !get_node(end_idx)->is_ReachabilityFence()) { end_idx--; } @@ -1428,7 +1430,7 @@ void UnionFind::extend( uint from_idx, uint to_idx ) { if( from_idx >= _max ) { uint size = 16; while( size <= from_idx ) size <<=1; - _indices = REALLOC_RESOURCE_ARRAY( uint, _indices, _max, size ); + _indices = REALLOC_RESOURCE_ARRAY( _indices, _max, size ); _max = size; } while( _cnt <= from_idx ) _indices[_cnt++] = 0; diff --git a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp index e0833afa29d..dacc8ce9c26 100644 --- a/src/hotspot/share/opto/c2_globals.hpp +++ b/src/hotspot/share/opto/c2_globals.hpp @@ -76,6 +76,17 @@ develop(bool, StressBailout, false, \ "Perform bailouts randomly at C2 failing() checks") \ \ + product(bool, OptimizeReachabilityFences, true, DIAGNOSTIC, \ + "Optimize reachability fences " \ + "(leave reachability fence nodes intact when turned off)") \ + \ + product(bool, PreserveReachabilityFencesOnConstants, false, DIAGNOSTIC, \ + "Keep reachability fences on compile-time constants") \ + \ + product(bool, StressReachabilityFences, false, DIAGNOSTIC, \ + "Aggressively insert reachability fences " \ + "for all oop method arguments") \ + \ develop(uint, StressBailoutMean, 100000, \ "The expected number of failing() checks made until " \ "a random bailout.") \ @@ -705,6 +716,10 @@ develop(bool, TraceIterativeGVN, false, \ "Print progress during Iterative Global Value Numbering") \ \ + develop(bool, UseDeepIGVNRevisit, true, \ + "Re-process nodes that could benefit from a deep revisit after " \ + "the IGVN worklist drains") \ + \ develop(uint, VerifyIterativeGVN, 0, \ "Verify Iterative Global Value Numbering =FEDCBA, with:" \ " F: verify Node::Ideal does not return nullptr if the node" \ @@ -907,6 +922,47 @@ \ develop(bool, StressLoopPeeling, false, \ "Randomize loop peeling decision") \ + \ + develop(bool, StressCountedLoop, false, \ + "Randomly delay conversion to counted loops") \ + \ + product(bool, HotCodeHeap, false, EXPERIMENTAL, \ + "Enable the code heap for hot C2 nmethods") \ + \ + product(double, HotCodeSamplePercent, 80, EXPERIMENTAL, \ + "Minimum percentage of profiling samples that must be in " \ + "the MethodHot heap before stopping hot code collection") \ + range(0, 100) \ + \ + product(double, HotCodeStablePercent, 5, EXPERIMENTAL, \ + "Maximum percentage of newly compiled to total C2 nmethods " \ + "to treat nmethod count as stable. " \ + "Values less than zero disable the stable check") \ + range(-1, DBL_MAX) \ + \ + product(uint, HotCodeIntervalSeconds, 300, EXPERIMENTAL, \ + "Seconds between hot code grouping attempts") \ + range(0, max_juint) \ + \ + product(uint, HotCodeSampleSeconds, 120, EXPERIMENTAL, \ + "Seconds to sample application threads per grouping attempt") \ + range(0, max_juint) \ + \ + product(uint, HotCodeStartupDelaySeconds, 120, EXPERIMENTAL, \ + "Seconds to delay before starting hot code grouping thread") \ + range(0, max_juint) \ + \ + product(uint, HotCodeMinSamplingMs, 5, EXPERIMENTAL, \ + "Minimum sampling interval in milliseconds") \ + range(0, max_juint) \ + \ + product(uint, HotCodeMaxSamplingMs, 15, EXPERIMENTAL, \ + "Maximum sampling interval in milliseconds") \ + range(0, max_juint) \ + \ + product(uint, HotCodeCallLevel, 1, EXPERIMENTAL, \ + "Number of levels of callees to relocate per candidate") \ + range(0, max_juint) \ // end of C2_FLAGS diff --git a/src/hotspot/share/opto/c2compiler.cpp b/src/hotspot/share/opto/c2compiler.cpp index ead1b78cdea..5d170f919c8 100644 --- a/src/hotspot/share/opto/c2compiler.cpp +++ b/src/hotspot/share/opto/c2compiler.cpp @@ -775,6 +775,7 @@ bool C2Compiler::is_intrinsic_supported(vmIntrinsics::ID id) { case vmIntrinsics::_longBitsToDouble: case vmIntrinsics::_Reference_get0: case vmIntrinsics::_Reference_refersTo0: + case vmIntrinsics::_Reference_reachabilityFence: case vmIntrinsics::_PhantomReference_refersTo0: case vmIntrinsics::_Reference_clear0: case vmIntrinsics::_PhantomReference_clear0: diff --git a/src/hotspot/share/opto/callGenerator.cpp b/src/hotspot/share/opto/callGenerator.cpp index 1465da02ac8..49897ca3c17 100644 --- a/src/hotspot/share/opto/callGenerator.cpp +++ b/src/hotspot/share/opto/callGenerator.cpp @@ -611,6 +611,20 @@ void CallGenerator::do_late_inline_helper() { } Compile* C = Compile::current(); + + uint endoff = call->jvms()->endoff(); + if (C->inlining_incrementally()) { + // No reachability edges should be present when incremental inlining takes place. + // Inlining logic doesn't expect any extra edges past debug info and fails with + // an assert in SafePointNode::grow_stack. + assert(endoff == call->req(), "reachability edges not supported"); + } else { + if (call->req() > endoff) { // reachability edges present + assert(OptimizeReachabilityFences, "required"); + return; // keep the original call node as the holder of reachability info + } + } + // Remove inlined methods from Compiler's lists. if (call->is_macro()) { C->remove_macro_node(call); diff --git a/src/hotspot/share/opto/callnode.cpp b/src/hotspot/share/opto/callnode.cpp index e01feb874ef..eb4f506d14f 100644 --- a/src/hotspot/share/opto/callnode.cpp +++ b/src/hotspot/share/opto/callnode.cpp @@ -43,6 +43,7 @@ #include "opto/rootnode.hpp" #include "opto/runtime.hpp" #include "runtime/sharedRuntime.hpp" +#include "runtime/stubRoutines.hpp" #include "utilities/powerOfTwo.hpp" // Portions of code courtesy of Clifford Click @@ -826,7 +827,7 @@ uint CallNode::match_edge(uint idx) const { // Determine whether the call could modify the field of the specified // instance at the specified offset. // -bool CallNode::may_modify(const TypeOopPtr* t_oop, PhaseValues* phase) { +bool CallNode::may_modify(const TypeOopPtr* t_oop, PhaseValues* phase) const { assert((t_oop != nullptr), "sanity"); if (is_call_to_arraycopystub() && strcmp(_name, "unsafe_arraycopy") != 0) { const TypeTuple* args = _tf->domain(); @@ -897,7 +898,7 @@ bool CallNode::may_modify(const TypeOopPtr* t_oop, PhaseValues* phase) { } // Does this call have a direct reference to n other than debug information? -bool CallNode::has_non_debug_use(Node *n) { +bool CallNode::has_non_debug_use(const Node *n) { const TypeTuple * d = tf()->domain(); for (uint i = TypeFunc::Parms; i < d->cnt(); i++) { Node *arg = in(i); @@ -939,7 +940,7 @@ Node *CallNode::result_cast() { } -void CallNode::extract_projections(CallProjections* projs, bool separate_io_proj, bool do_asserts) const { +void CallNode::extract_projections(CallProjections* projs, bool separate_io_proj, bool do_asserts, bool allow_handlers) const { projs->fallthrough_proj = nullptr; projs->fallthrough_catchproj = nullptr; projs->fallthrough_ioproj = nullptr; @@ -960,14 +961,13 @@ void CallNode::extract_projections(CallProjections* projs, bool separate_io_proj projs->fallthrough_proj = pn; const Node* cn = pn->unique_ctrl_out_or_null(); if (cn != nullptr && cn->is_Catch()) { - ProjNode *cpn = nullptr; for (DUIterator_Fast kmax, k = cn->fast_outs(kmax); k < kmax; k++) { - cpn = cn->fast_out(k)->as_Proj(); - assert(cpn->is_CatchProj(), "must be a CatchProjNode"); - if (cpn->_con == CatchProjNode::fall_through_index) + CatchProjNode* cpn = cn->fast_out(k)->as_CatchProj(); + assert(allow_handlers || !cpn->is_handler_proj(), "not allowed"); + if (cpn->_con == CatchProjNode::fall_through_index) { + assert(cpn->handler_bci() == CatchProjNode::no_handler_bci, ""); projs->fallthrough_catchproj = cpn; - else { - assert(cpn->_con == CatchProjNode::catch_all_index, "must be correct index."); + } else if (!cpn->is_handler_proj()) { projs->catchall_catchproj = cpn; } } @@ -975,15 +975,20 @@ void CallNode::extract_projections(CallProjections* projs, bool separate_io_proj break; } case TypeFunc::I_O: - if (pn->_is_io_use) + if (pn->_is_io_use) { projs->catchall_ioproj = pn; - else + } else { projs->fallthrough_ioproj = pn; + } for (DUIterator j = pn->outs(); pn->has_out(j); j++) { Node* e = pn->out(j); - if (e->Opcode() == Op_CreateEx && e->in(0)->is_CatchProj() && e->outcnt() > 0) { - assert(projs->exobj == nullptr, "only one"); - projs->exobj = e; + if (e->Opcode() == Op_CreateEx && e->outcnt() > 0) { + CatchProjNode* ecpn = e->in(0)->isa_CatchProj(); + assert(allow_handlers || ecpn == nullptr || !ecpn->is_handler_proj(), "not allowed"); + if (ecpn != nullptr && ecpn->_con != CatchProjNode::fall_through_index && !ecpn->is_handler_proj()) { + assert(projs->exobj == nullptr, "only one"); + projs->exobj = e; + } } } break; @@ -1371,6 +1376,25 @@ TupleNode* CallLeafPureNode::make_tuple_of_input_state_and_top_return_values(con return tuple; } +CallLeafPureNode* CallLeafPureNode::inline_call_leaf_pure_node(Node* control) const { + Node* top = Compile::current()->top(); + if (control == nullptr) { + control = in(TypeFunc::Control); + } + + CallLeafPureNode* call = new CallLeafPureNode(tf(), entry_point(), _name); + call->init_req(TypeFunc::Control, control); + call->init_req(TypeFunc::I_O, top); + call->init_req(TypeFunc::Memory, top); + call->init_req(TypeFunc::ReturnAdr, top); + call->init_req(TypeFunc::FramePtr, top); + for (unsigned int i = 0; i < tf()->domain()->cnt() - TypeFunc::Parms; i++) { + call->init_req(TypeFunc::Parms + i, in(TypeFunc::Parms + i)); + } + + return call; +} + Node* CallLeafPureNode::Ideal(PhaseGVN* phase, bool can_reshape) { if (is_dead()) { return nullptr; @@ -1589,6 +1613,33 @@ void SafePointNode::disconnect_from_root(PhaseIterGVN *igvn) { } } +void SafePointNode::remove_non_debug_edges(NodeEdgeTempStorage& non_debug_edges) { + assert(non_debug_edges._state == NodeEdgeTempStorage::state_initial, "not processed"); + assert(non_debug_edges.is_empty(), "edges not processed"); + + while (req() > jvms()->endoff()) { + uint last = req() - 1; + non_debug_edges.push(in(last)); + del_req(last); + } + + assert(jvms()->endoff() == req(), "no extra edges past debug info allowed"); + DEBUG_ONLY(non_debug_edges._state = NodeEdgeTempStorage::state_populated); +} + +void SafePointNode::restore_non_debug_edges(NodeEdgeTempStorage& non_debug_edges) { + assert(non_debug_edges._state == NodeEdgeTempStorage::state_populated, "not populated"); + assert(jvms()->endoff() == req(), "no extra edges past debug info allowed"); + + while (!non_debug_edges.is_empty()) { + Node* non_debug_edge = non_debug_edges.pop(); + add_req(non_debug_edge); + } + + assert(non_debug_edges.is_empty(), "edges not processed"); + DEBUG_ONLY(non_debug_edges._state = NodeEdgeTempStorage::state_processed); +} + //============== SafePointScalarObjectNode ============== SafePointScalarObjectNode::SafePointScalarObjectNode(const TypeOopPtr* tp, Node* alloc, uint first_index, uint depth, uint n_fields) : @@ -2393,7 +2444,7 @@ void AbstractLockNode::log_lock_optimization(Compile *C, const char * tag, Node* } } -bool CallNode::may_modify_arraycopy_helper(const TypeOopPtr* dest_t, const TypeOopPtr* t_oop, PhaseValues* phase) { +bool CallNode::may_modify_arraycopy_helper(const TypeOopPtr* dest_t, const TypeOopPtr* t_oop, PhaseValues* phase) const { if (dest_t->is_known_instance() && t_oop->is_known_instance()) { return dest_t->instance_id() == t_oop->instance_id(); } @@ -2437,3 +2488,157 @@ bool CallNode::may_modify_arraycopy_helper(const TypeOopPtr* dest_t, const TypeO return true; } + +PowDNode::PowDNode(Compile* C, Node* base, Node* exp) + : CallLeafPureNode( + OptoRuntime::Math_DD_D_Type(), + StubRoutines::dpow() != nullptr ? StubRoutines::dpow() : CAST_FROM_FN_PTR(address, SharedRuntime::dpow), + "pow") { + add_flag(Flag_is_macro); + C->add_macro_node(this); + + init_req(TypeFunc::Parms + 0, base); + init_req(TypeFunc::Parms + 1, C->top()); // double slot padding + init_req(TypeFunc::Parms + 2, exp); + init_req(TypeFunc::Parms + 3, C->top()); // double slot padding +} + +const Type* PowDNode::Value(PhaseGVN* phase) const { + const Type* t_base = phase->type(base()); + const Type* t_exp = phase->type(exp()); + + if (t_base == Type::TOP || t_exp == Type::TOP) { + return Type::TOP; + } + + const TypeD* base_con = t_base->isa_double_constant(); + const TypeD* exp_con = t_exp->isa_double_constant(); + const TypeD* result_t = nullptr; + + // constant folding: both inputs are constants + if (base_con != nullptr && exp_con != nullptr) { + result_t = TypeD::make(SharedRuntime::dpow(base_con->getd(), exp_con->getd())); + } + + // Special cases when only the exponent is known: + if (exp_con != nullptr) { + double e = exp_con->getd(); + + // If the second argument is positive or negative zero, then the result is 1.0. + // i.e., pow(x, +/-0.0D) => 1.0 + if (e == 0.0) { // true for both -0.0 and +0.0 + result_t = TypeD::ONE; + } + + // If the second argument is NaN, then the result is NaN. + // i.e., pow(x, NaN) => NaN + if (g_isnan(e)) { + result_t = TypeD::make(NAN); + } + } + + if (result_t != nullptr) { + // We can't simply return a TypeD here, it must be a tuple type to be compatible with call nodes. + const Type** fields = TypeTuple::fields(2); + fields[TypeFunc::Parms + 0] = result_t; + fields[TypeFunc::Parms + 1] = Type::HALF; + return TypeTuple::make(TypeFunc::Parms + 2, fields); + } + + return tf()->range(); +} + +Node* PowDNode::Ideal(PhaseGVN* phase, bool can_reshape) { + if (!can_reshape) { + return nullptr; // wait for igvn + } + + PhaseIterGVN* igvn = phase->is_IterGVN(); + Node* base = this->base(); + Node* exp = this->exp(); + + const Type* t_exp = phase->type(exp); + const TypeD* exp_con = t_exp->isa_double_constant(); + + // Special cases when only the exponent is known: + if (exp_con != nullptr) { + double e = exp_con->getd(); + + // If the second argument is 1.0, then the result is the same as the first argument. + // i.e., pow(x, 1.0) => x + if (e == 1.0) { + return make_tuple_of_input_state_and_result(igvn, base); + } + + // If the second argument is 2.0, then strength reduce to multiplications. + // i.e., pow(x, 2.0) => x * x + if (e == 2.0) { + Node* mul = igvn->transform(new MulDNode(base, base)); + return make_tuple_of_input_state_and_result(igvn, mul); + } + + // If the second argument is 0.5, the strength reduce to square roots. + // i.e., pow(x, 0.5) => sqrt(x) iff x > 0 + if (e == 0.5 && Matcher::match_rule_supported(Op_SqrtD)) { + Node* ctrl = in(TypeFunc::Control); + Node* zero = igvn->zerocon(T_DOUBLE); + + // According to the API specs, pow(-0.0, 0.5) = 0.0 and sqrt(-0.0) = -0.0. + // So pow(-0.0, 0.5) shouldn't be replaced with sqrt(-0.0). + // -0.0/+0.0 are both excluded since floating-point comparison doesn't distinguish -0.0 from +0.0. + Node* cmp = igvn->register_new_node_with_optimizer(new CmpDNode(base, zero)); + Node* test = igvn->register_new_node_with_optimizer(new BoolNode(cmp, BoolTest::le)); + + IfNode* iff = new IfNode(ctrl, test, PROB_UNLIKELY_MAG(3), COUNT_UNKNOWN); + igvn->register_new_node_with_optimizer(iff); + Node* if_slow = igvn->register_new_node_with_optimizer(new IfTrueNode(iff)); // x <= 0 + Node* if_fast = igvn->register_new_node_with_optimizer(new IfFalseNode(iff)); // x > 0 + + // slow path: call pow(x, 0.5) + Node* call = igvn->register_new_node_with_optimizer(inline_call_leaf_pure_node(if_slow)); + Node* call_ctrl = igvn->register_new_node_with_optimizer(new ProjNode(call, TypeFunc::Control)); + Node* call_result = igvn->register_new_node_with_optimizer(new ProjNode(call, TypeFunc::Parms + 0)); + + // fast path: sqrt(x) + Node* sqrt = igvn->register_new_node_with_optimizer(new SqrtDNode(igvn->C, if_fast, base)); + + // merge paths + RegionNode* region = new RegionNode(3); + igvn->register_new_node_with_optimizer(region); + region->init_req(1, call_ctrl); // slow path + region->init_req(2, if_fast); // fast path + + PhiNode* phi = new PhiNode(region, Type::DOUBLE); + igvn->register_new_node_with_optimizer(phi); + phi->init_req(1, call_result); // slow: pow() result + phi->init_req(2, sqrt); // fast: sqrt() result + + igvn->C->set_has_split_ifs(true); // Has chance for split-if optimization + + return make_tuple_of_input_state_and_result(igvn, phi, region); + } + } + + return CallLeafPureNode::Ideal(phase, can_reshape); +} + +// We can't simply have Ideal() returning a Con or MulNode since the users are still expecting a Call node, but we could +// produce a tuple that follows the same pattern so users can still get control, io, memory, etc.. +TupleNode* PowDNode::make_tuple_of_input_state_and_result(PhaseIterGVN* phase, Node* result, Node* control) { + if (control == nullptr) { + control = in(TypeFunc::Control); + } + + Compile* C = phase->C; + C->remove_macro_node(this); + TupleNode* tuple = TupleNode::make( + tf()->range(), + control, + in(TypeFunc::I_O), + in(TypeFunc::Memory), + in(TypeFunc::FramePtr), + in(TypeFunc::ReturnAdr), + result, + C->top()); + return tuple; +} diff --git a/src/hotspot/share/opto/callnode.hpp b/src/hotspot/share/opto/callnode.hpp index 95d1fc27d45..e4c548fc744 100644 --- a/src/hotspot/share/opto/callnode.hpp +++ b/src/hotspot/share/opto/callnode.hpp @@ -503,6 +503,66 @@ public: return _has_ea_local_in_scope; } + // A temporary storge for node edges. + // Intended for a single use. + class NodeEdgeTempStorage : public StackObj { + friend class SafePointNode; + + PhaseIterGVN& _igvn; + Node* _node_hook; + +#ifdef ASSERT + enum State { state_initial, state_populated, state_processed }; + + State _state; // monotonically transitions from initial to processed state. +#endif // ASSERT + + bool is_empty() const { + return _node_hook == nullptr || _node_hook->req() == 1; + } + void push(Node* n) { + assert(n != nullptr, ""); + if (_node_hook == nullptr) { + _node_hook = new Node(nullptr); + } + _node_hook->add_req(n); + } + Node* pop() { + assert(!is_empty(), ""); + int idx = _node_hook->req()-1; + Node* r = _node_hook->in(idx); + _node_hook->del_req(idx); + assert(r != nullptr, ""); + return r; + } + + public: + NodeEdgeTempStorage(PhaseIterGVN &igvn) : _igvn(igvn), _node_hook(nullptr) + DEBUG_ONLY(COMMA _state(state_initial)) { + assert(is_empty(), ""); + } + + ~NodeEdgeTempStorage() { + assert(_state == state_processed, "not processed"); + assert(is_empty(), ""); + if (_node_hook != nullptr) { + _node_hook->destruct(&_igvn); + } + } + + void remove_edge_if_present(Node* n) { + if (!is_empty()) { + int idx = _node_hook->find_edge(n); + if (idx > 0) { + _node_hook->del_req(idx); + } + } + } + }; + + void remove_non_debug_edges(NodeEdgeTempStorage& non_debug_edges); + void restore_non_debug_edges(NodeEdgeTempStorage& non_debug_edges); + void disconnect_from_root(PhaseIterGVN *igvn); // Standard Node stuff @@ -685,7 +745,7 @@ class CallGenerator; class CallNode : public SafePointNode { protected: - bool may_modify_arraycopy_helper(const TypeOopPtr* dest_t, const TypeOopPtr* t_oop, PhaseValues* phase); + bool may_modify_arraycopy_helper(const TypeOopPtr* dest_t, const TypeOopPtr* t_oop, PhaseValues* phase) const; public: const TypeFunc* _tf; // Function type @@ -734,9 +794,9 @@ public: virtual bool needs_deep_clone_jvms(Compile* C) { return _generator != nullptr || C->needs_deep_clone_jvms(); } // Returns true if the call may modify n - virtual bool may_modify(const TypeOopPtr* t_oop, PhaseValues* phase); + virtual bool may_modify(const TypeOopPtr* t_oop, PhaseValues* phase) const; // Does this node have a use of n other than in debug information? - bool has_non_debug_use(Node* n); + bool has_non_debug_use(const Node* n); // Returns the unique CheckCastPP of a call // or result projection is there are several CheckCastPP // or returns null if there is no one. @@ -751,7 +811,10 @@ public: // Collect all the interesting edges from a call for use in // replacing the call by something else. Used by macro expansion // and the late inlining support. - void extract_projections(CallProjections* projs, bool separate_io_proj, bool do_asserts = true) const; + void extract_projections(CallProjections* projs, + bool separate_io_proj, + bool do_asserts = true, + bool allow_handlers = false) const; virtual uint match_edge(uint idx) const; @@ -948,6 +1011,8 @@ public: } int Opcode() const override; Node* Ideal(PhaseGVN* phase, bool can_reshape) override; + + CallLeafPureNode* inline_call_leaf_pure_node(Node* control = nullptr) const; }; //------------------------------CallLeafNoFPNode------------------------------- @@ -1042,7 +1107,7 @@ public: virtual bool guaranteed_safepoint() { return false; } // allocations do not modify their arguments - virtual bool may_modify(const TypeOopPtr* t_oop, PhaseValues* phase) { return false;} + virtual bool may_modify(const TypeOopPtr* t_oop, PhaseValues* phase) const { return false; } // Pattern-match a possible usage of AllocateNode. // Return null if no allocation is recognized. @@ -1206,7 +1271,7 @@ public: bool is_balanced(); // locking does not modify its arguments - virtual bool may_modify(const TypeOopPtr* t_oop, PhaseValues* phase){ return false; } + virtual bool may_modify(const TypeOopPtr* t_oop, PhaseValues* phase) const { return false; } #ifndef PRODUCT void create_lock_counter(JVMState* s); @@ -1299,4 +1364,19 @@ public: JVMState* dbg_jvms() const { return nullptr; } #endif }; + +//------------------------------PowDNode-------------------------------------- +class PowDNode : public CallLeafPureNode { + TupleNode* make_tuple_of_input_state_and_result(PhaseIterGVN* phase, Node* result, Node* control = nullptr); + +public: + PowDNode(Compile* C, Node* base, Node* exp); + int Opcode() const override; + const Type* Value(PhaseGVN* phase) const override; + Node* Ideal(PhaseGVN* phase, bool can_reshape) override; + + Node* base() const { return in(TypeFunc::Parms + 0); } + Node* exp() const { return in(TypeFunc::Parms + 2); } +}; + #endif // SHARE_OPTO_CALLNODE_HPP diff --git a/src/hotspot/share/opto/castnode.cpp b/src/hotspot/share/opto/castnode.cpp index 54269a56c19..7bb6b1dcb77 100644 --- a/src/hotspot/share/opto/castnode.cpp +++ b/src/hotspot/share/opto/castnode.cpp @@ -413,6 +413,43 @@ Node* CastLLNode::Ideal(PhaseGVN* phase, bool can_reshape) { return nullptr; } +// CastPPNodes are removed before matching, while alias classes are needed in global code motion. +// As a result, it is not valid for a CastPPNode to change the oop such that the derived pointers +// lie in different alias classes with and without the node. For example, a CastPPNode c may not +// cast an Object to a Bottom[], because later removal of c would affect the alias class of c's +// array length field (c + arrayOopDesc::length_offset_in_bytes()). +// +// This function verifies that a CastPPNode on an oop does not violate the aforementioned property. +// +// TODO 8382147: Currently, this verification only applies during the construction of a CastPPNode, +// we may want to apply the same verification during IGVN transformations, as well as final graph +// reshaping. +void CastPPNode::verify_type(const Type* in_type, const Type* out_type) { +#ifdef ASSERT + out_type = out_type->join(in_type); + if (in_type->empty() || out_type->empty()) { + return; + } + if (in_type == TypePtr::NULL_PTR || out_type == TypePtr::NULL_PTR) { + return; + } + if (!in_type->isa_oopptr() && !out_type->isa_oopptr()) { + return; + } + + assert(in_type->isa_oopptr() && out_type->isa_oopptr(), "must be both oops or both non-oops"); + if (in_type->isa_aryptr() && out_type->isa_aryptr()) { + const Type* e1 = in_type->is_aryptr()->elem(); + const Type* e2 = out_type->is_aryptr()->elem(); + assert(e1->basic_type() == e2->basic_type(), "must both be arrays of the same primitive type or both be oops arrays"); + return; + } + + assert(in_type->isa_instptr() && out_type->isa_instptr(), "must be both array oops or both non-array oops"); + assert(in_type->is_instptr()->instance_klass() == out_type->is_instptr()->instance_klass(), "must not cast to a different type"); +#endif // ASSERT +} + //------------------------------Value------------------------------------------ // Take 'join' of input and cast-up type, unless working with an Interface const Type* CheckCastPPNode::Value(PhaseGVN* phase) const { @@ -440,6 +477,11 @@ const Type* CheckCastPPNode::Value(PhaseGVN* phase) const { return result; } +Node* CheckCastPPNode::pin_node_under_control_impl() const { + assert(_dependency.is_floating(), "already pinned"); + return new CheckCastPPNode(in(0), in(1), bottom_type(), _dependency.with_pinned_dependency(), _extra_types); +} + //============================================================================= //------------------------------Value------------------------------------------ const Type* CastX2PNode::Value(PhaseGVN* phase) const { diff --git a/src/hotspot/share/opto/castnode.hpp b/src/hotspot/share/opto/castnode.hpp index 38545fd6f41..dce54eb73c0 100644 --- a/src/hotspot/share/opto/castnode.hpp +++ b/src/hotspot/share/opto/castnode.hpp @@ -303,14 +303,18 @@ public: //------------------------------CastPPNode------------------------------------- // cast pointer to pointer (different type) -class CastPPNode: public ConstraintCastNode { - public: - CastPPNode (Node* ctrl, Node* n, const Type* t, const DependencyType& dependency = DependencyType::FloatingNarrowing, const TypeTuple* types = nullptr) +class CastPPNode : public ConstraintCastNode { +public: + CastPPNode(Node* ctrl, Node* n, const Type* t, const DependencyType& dependency = DependencyType::FloatingNarrowing, const TypeTuple* types = nullptr) : ConstraintCastNode(ctrl, n, t, dependency, types) { init_class_id(Class_CastPP); + verify_type(n->bottom_type(), t); } virtual int Opcode() const; virtual uint ideal_reg() const { return Op_RegP; } + +private: + static void verify_type(const Type* in_type, const Type* out_type); }; //------------------------------CheckCastPPNode-------------------------------- @@ -329,6 +333,7 @@ class CheckCastPPNode: public ConstraintCastNode { private: virtual bool depends_only_on_test_impl() const { return !type()->isa_rawptr() && ConstraintCastNode::depends_only_on_test_impl(); } + virtual Node* pin_node_under_control_impl() const; }; diff --git a/src/hotspot/share/opto/cfgnode.cpp b/src/hotspot/share/opto/cfgnode.cpp index a251a253ed1..828e5bf299f 100644 --- a/src/hotspot/share/opto/cfgnode.cpp +++ b/src/hotspot/share/opto/cfgnode.cpp @@ -735,7 +735,7 @@ Node *RegionNode::Ideal(PhaseGVN *phase, bool can_reshape) { #endif } // Remove the RegionNode itself from DefUse info - igvn->remove_dead_node(this); + igvn->remove_dead_node(this, PhaseIterGVN::NodeOrigin::Graph); return nullptr; } return this; // Record progress @@ -1007,7 +1007,7 @@ bool RegionNode::optimize_trichotomy(PhaseIterGVN* igvn) { BoolNode* new_bol = new BoolNode(bol2->in(1), res); igvn->replace_input_of(iff2, 1, igvn->transform((proj2->_con == 1) ? new_bol : new_bol->negate(igvn))); if (new_bol->outcnt() == 0) { - igvn->remove_dead_node(new_bol); + igvn->remove_dead_node(new_bol, PhaseIterGVN::NodeOrigin::Speculative); } } return false; @@ -2689,7 +2689,7 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { #ifdef _LP64 // Push DecodeN/DecodeNKlass down through phi. // The rest of phi graph will transform by split EncodeP node though phis up. - if ((UseCompressedOops || UseCompressedClassPointers) && can_reshape && progress == nullptr) { + if (can_reshape && progress == nullptr) { bool may_push = true; bool has_decodeN = false; bool is_decodeN = false; diff --git a/src/hotspot/share/opto/chaitin.cpp b/src/hotspot/share/opto/chaitin.cpp index 3ba3ffc1045..606d45d4a12 100644 --- a/src/hotspot/share/opto/chaitin.cpp +++ b/src/hotspot/share/opto/chaitin.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -265,7 +265,7 @@ PhaseChaitin::PhaseChaitin(uint unique, PhaseCFG &cfg, Matcher &matcher, bool sc assert((&buckets[0][0] + nr_blocks) == offset, "should be"); // Free the now unused memory - FREE_RESOURCE_ARRAY(Block*, buckets[1], (NUMBUCKS-1)*nr_blocks); + FREE_RESOURCE_ARRAY(buckets[1], (NUMBUCKS-1)*nr_blocks); // Finally, point the _blks to our memory _blks = buckets[0]; @@ -2633,7 +2633,7 @@ void PhaseChaitin::verify_base_ptrs(ResourceArea* a) const { #ifdef _LP64 (UseCompressedOops && check->as_Mach()->ideal_Opcode() == Op_CastPP) || (UseCompressedOops && check->as_Mach()->ideal_Opcode() == Op_DecodeN) || - (UseCompressedClassPointers && check->as_Mach()->ideal_Opcode() == Op_DecodeNKlass) || + (check->as_Mach()->ideal_Opcode() == Op_DecodeNKlass) || #endif // _LP64 check->as_Mach()->ideal_Opcode() == Op_LoadP || check->as_Mach()->ideal_Opcode() == Op_LoadKlass))) { diff --git a/src/hotspot/share/opto/classes.cpp b/src/hotspot/share/opto/classes.cpp index b760a179b57..1cd6c52393b 100644 --- a/src/hotspot/share/opto/classes.cpp +++ b/src/hotspot/share/opto/classes.cpp @@ -43,6 +43,7 @@ #include "opto/narrowptrnode.hpp" #include "opto/node.hpp" #include "opto/opaquenode.hpp" +#include "opto/reachability.hpp" #include "opto/rootnode.hpp" #include "opto/subnode.hpp" #include "opto/subtypenode.hpp" diff --git a/src/hotspot/share/opto/classes.hpp b/src/hotspot/share/opto/classes.hpp index 719b90ad6dd..0f67cf90183 100644 --- a/src/hotspot/share/opto/classes.hpp +++ b/src/hotspot/share/opto/classes.hpp @@ -286,6 +286,7 @@ macro(OpaqueZeroTripGuard) macro(OpaqueConstantBool) macro(OpaqueInitializedAssertionPredicate) macro(OpaqueTemplateAssertionPredicate) +macro(PowD) macro(ProfileBoolean) macro(OrI) macro(OrL) @@ -395,6 +396,7 @@ macro(AddVL) macro(AddReductionVL) macro(AddVF) macro(AddVHF) +macro(AddReductionVHF) macro(AddReductionVF) macro(AddVD) macro(AddReductionVD) @@ -412,6 +414,7 @@ macro(MulReductionVI) macro(MulVL) macro(MulReductionVL) macro(MulVF) +macro(MulReductionVHF) macro(MulReductionVF) macro(MulVD) macro(MulReductionVD) @@ -546,3 +549,4 @@ macro(MaskAll) macro(AndVMask) macro(OrVMask) macro(XorVMask) +macro(ReachabilityFence) diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index f1ea8231df9..382c8f89a5f 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -74,6 +74,7 @@ #include "opto/output.hpp" #include "opto/parse.hpp" #include "opto/phaseX.hpp" +#include "opto/reachability.hpp" #include "opto/rootnode.hpp" #include "opto/runtime.hpp" #include "opto/stringopts.hpp" @@ -396,6 +397,9 @@ void Compile::remove_useless_node(Node* dead) { if (dead->is_expensive()) { remove_expensive_node(dead); } + if (dead->is_ReachabilityFence()) { + remove_reachability_fence(dead->as_ReachabilityFence()); + } if (dead->is_OpaqueTemplateAssertionPredicate()) { remove_template_assertion_predicate_opaque(dead->as_OpaqueTemplateAssertionPredicate()); } @@ -459,6 +463,7 @@ void Compile::disconnect_useless_nodes(Unique_Node_List& useful, Unique_Node_Lis // Remove useless Template Assertion Predicate opaque nodes remove_useless_nodes(_template_assertion_predicate_opaques, useful); remove_useless_nodes(_expensive_nodes, useful); // remove useless expensive nodes + remove_useless_nodes(_reachability_fences, useful); // remove useless node recorded for post loop opts IGVN pass remove_useless_nodes(_for_post_loop_igvn, useful); // remove useless node recorded for post loop opts IGVN pass remove_useless_nodes(_for_merge_stores_igvn, useful); // remove useless node recorded for merge stores IGVN pass remove_useless_unstable_if_traps(useful); // remove useless unstable_if traps @@ -665,6 +670,7 @@ Compile::Compile(ciEnv* ci_env, ciMethod* target, int osr_bci, _parse_predicates(comp_arena(), 8, 0, nullptr), _template_assertion_predicate_opaques(comp_arena(), 8, 0, nullptr), _expensive_nodes(comp_arena(), 8, 0, nullptr), + _reachability_fences(comp_arena(), 8, 0, nullptr), _for_post_loop_igvn(comp_arena(), 8, 0, nullptr), _for_merge_stores_igvn(comp_arena(), 8, 0, nullptr), _unstable_if_traps(comp_arena(), 8, 0, nullptr), @@ -741,7 +747,7 @@ Compile::Compile(ciEnv* ci_env, ciMethod* target, int osr_bci, if (StressLCM || StressGCM || StressIGVN || StressCCP || StressIncrementalInlining || StressMacroExpansion || StressMacroElimination || StressUnstableIfTraps || - StressBailout || StressLoopPeeling) { + StressBailout || StressLoopPeeling || StressCountedLoop) { initialize_stress_seed(directive); } @@ -934,6 +940,7 @@ Compile::Compile(ciEnv* ci_env, _directive(directive), _log(ci_env->log()), _first_failure_details(nullptr), + _reachability_fences(comp_arena(), 8, 0, nullptr), _for_post_loop_igvn(comp_arena(), 8, 0, nullptr), _for_merge_stores_igvn(comp_arena(), 8, 0, nullptr), _congraph(nullptr), @@ -1611,7 +1618,7 @@ void Compile::grow_alias_types() { const int new_ats = old_ats; // how many more? const int grow_ats = old_ats+new_ats; // how many now? _max_alias_types = grow_ats; - _alias_types = REALLOC_ARENA_ARRAY(comp_arena(), AliasType*, _alias_types, old_ats, grow_ats); + _alias_types = REALLOC_ARENA_ARRAY(comp_arena(), _alias_types, old_ats, grow_ats); AliasType* ats = NEW_ARENA_ARRAY(comp_arena(), AliasType, new_ats); Copy::zero_to_bytes(ats, sizeof(AliasType)*new_ats); for (int i = 0; i < new_ats; i++) _alias_types[old_ats+i] = &ats[i]; @@ -2257,7 +2264,9 @@ bool Compile::optimize_loops(PhaseIterGVN& igvn, LoopOptsMode mode) { PhaseIdealLoop::optimize(igvn, mode); _loop_opts_cnt--; if (failing()) return false; - if (major_progress()) print_method(PHASE_PHASEIDEALLOOP_ITERATIONS, 2); + if (major_progress()) { + print_method(PHASE_PHASEIDEALLOOP_ITERATIONS, 2); + } } } return true; @@ -2275,7 +2284,7 @@ void Compile::remove_root_to_sfpts_edges(PhaseIterGVN& igvn) { if (n != nullptr && n->is_SafePoint()) { r->rm_prec(i); if (n->outcnt() == 0) { - igvn.remove_dead_node(n); + igvn.remove_dead_node(n, PhaseIterGVN::NodeOrigin::Graph); } --i; } @@ -2319,7 +2328,7 @@ void Compile::Optimize() { #endif { TracePhase tp(_t_iterGVN); - igvn.optimize(); + igvn.optimize(true); } if (failing()) return; @@ -2383,7 +2392,7 @@ void Compile::Optimize() { PhaseRenumberLive prl(initial_gvn(), *igvn_worklist()); } igvn.reset(); - igvn.optimize(); + igvn.optimize(true); if (failing()) return; } @@ -2416,7 +2425,7 @@ void Compile::Optimize() { int mcount = macro_count(); // Record number of allocations and locks before IGVN // Optimize out fields loads from scalar replaceable allocations. - igvn.optimize(); + igvn.optimize(true); print_method(PHASE_ITER_GVN_AFTER_EA, 2); if (failing()) return; @@ -2496,7 +2505,7 @@ void Compile::Optimize() { { TracePhase tp(_t_iterGVN2); igvn.reset_from_igvn(&ccp); - igvn.optimize(); + igvn.optimize(true); } print_method(PHASE_ITER_GVN2, 2); @@ -2508,12 +2517,23 @@ void Compile::Optimize() { return; } - if (failing()) return; - C->clear_major_progress(); // ensure that major progress is now clear process_for_post_loop_opts_igvn(igvn); + if (failing()) return; + + // Once loop optimizations are over, it is safe to get rid of all reachability fence nodes and + // migrate reachability edges to safepoints. + if (OptimizeReachabilityFences && _reachability_fences.length() > 0) { + TracePhase tp1(_t_idealLoop); + TracePhase tp2(_t_reachability); + PhaseIdealLoop::optimize(igvn, PostLoopOptsExpandReachabilityFences); + print_method(PHASE_EXPAND_REACHABILITY_FENCES, 2); + if (failing()) return; + assert(_reachability_fences.length() == 0 || PreserveReachabilityFencesOnConstants, "no RF nodes allowed"); + } + process_for_merge_stores_igvn(igvn); if (failing()) return; @@ -3180,10 +3200,10 @@ void Compile::final_graph_reshaping_impl(Node *n, Final_Reshape_Counts& frc, Uni !n->in(2)->is_Con() ) { // right use is not a constant // Check for commutative opcode switch( nop ) { - case Op_AddI: case Op_AddF: case Op_AddD: case Op_AddL: + case Op_AddI: case Op_AddF: case Op_AddD: case Op_AddHF: case Op_AddL: case Op_MaxI: case Op_MaxL: case Op_MaxF: case Op_MaxD: case Op_MinI: case Op_MinL: case Op_MinF: case Op_MinD: - case Op_MulI: case Op_MulF: case Op_MulD: case Op_MulL: + case Op_MulI: case Op_MulF: case Op_MulD: case Op_MulHF: case Op_MulL: case Op_AndL: case Op_XorL: case Op_OrL: case Op_AndI: case Op_XorI: case Op_OrI: { // Move "last use" input to left by swapping inputs @@ -3262,6 +3282,8 @@ void Compile::handle_div_mod_op(Node* n, BasicType bt, bool is_unsigned) { void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& frc, uint nop, Unique_Node_List& dead_nodes) { switch( nop ) { // Count all float operations that may use FPU + case Op_AddHF: + case Op_MulHF: case Op_AddF: case Op_SubF: case Op_MulF: @@ -3412,8 +3434,7 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f Node *addp = n->in(AddPNode::Address); assert(n->as_AddP()->address_input_has_same_base(), "Base pointers must match (addp %u)", addp->_idx ); #ifdef _LP64 - if ((UseCompressedOops || UseCompressedClassPointers) && - addp->Opcode() == Op_ConP && + if (addp->Opcode() == Op_ConP && addp == n->in(AddPNode::Base) && n->in(AddPNode::Offset)->is_Con()) { // If the transformation of ConP to ConN+DecodeN is beneficial depends @@ -3426,7 +3447,7 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f bool is_klass = t->isa_klassptr() != nullptr; if ((is_oop && UseCompressedOops && Matcher::const_oop_prefer_decode() ) || - (is_klass && UseCompressedClassPointers && Matcher::const_klass_prefer_decode() && + (is_klass && Matcher::const_klass_prefer_decode() && t->isa_klassptr()->exact_klass()->is_in_encoding_range())) { Node* nn = nullptr; @@ -3769,10 +3790,12 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f case Op_AddReductionVI: case Op_AddReductionVL: + case Op_AddReductionVHF: case Op_AddReductionVF: case Op_AddReductionVD: case Op_MulReductionVI: case Op_MulReductionVL: + case Op_MulReductionVHF: case Op_MulReductionVF: case Op_MulReductionVD: case Op_MinReductionV: @@ -3798,7 +3821,11 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f } break; case Op_Loop: - assert(!n->as_Loop()->is_loop_nest_inner_loop() || _loop_opts_cnt == 0, "should have been turned into a counted loop"); + // When StressCountedLoop is enabled, this loop may intentionally avoid a counted loop conversion. + // This is expected behavior for the stress mode, which exercises alternative compilation paths. + if (!StressCountedLoop) { + assert(!n->as_Loop()->is_loop_nest_inner_loop() || _loop_opts_cnt == 0, "should have been turned into a counted loop"); + } case Op_CountedLoop: case Op_LongCountedLoop: case Op_OuterStripMinedLoop: @@ -3968,11 +3995,28 @@ void Compile::final_graph_reshaping_walk(Node_Stack& nstack, Node* root, Final_R } } + expand_reachability_edges(sfpt); + // Skip next transformation if compressed oops are not used. - if ((UseCompressedOops && !Matcher::gen_narrow_oop_implicit_null_checks()) || - (!UseCompressedOops && !UseCompressedClassPointers)) + if (UseCompressedOops && !Matcher::gen_narrow_oop_implicit_null_checks()) return; + // Go over ReachabilityFence nodes to skip DecodeN nodes for referents. + // The sole purpose of RF node is to keep the referent oop alive and + // decoding the oop for that is not needed. + for (int i = 0; i < C->reachability_fences_count(); i++) { + ReachabilityFenceNode* rf = C->reachability_fence(i); + DecodeNNode* dn = rf->in(1)->isa_DecodeN(); + if (dn != nullptr) { + if (!dn->has_non_debug_uses() || Matcher::narrow_oop_use_complex_address()) { + rf->set_req(1, dn->in(1)); + if (dn->outcnt() == 0) { + dn->disconnect_inputs(this); + } + } + } + } + // Go over safepoints nodes to skip DecodeN/DecodeNKlass nodes for debug edges. // It could be done for an uncommon traps or any safepoints/calls // if the DecodeN/DecodeNKlass node is referenced only in a debug info. @@ -3986,21 +4030,8 @@ void Compile::final_graph_reshaping_walk(Node_Stack& nstack, Node* root, Final_R n->as_CallStaticJava()->uncommon_trap_request() != 0); for (int j = start; j < end; j++) { Node* in = n->in(j); - if (in->is_DecodeNarrowPtr()) { - bool safe_to_skip = true; - if (!is_uncommon ) { - // Is it safe to skip? - for (uint i = 0; i < in->outcnt(); i++) { - Node* u = in->raw_out(i); - if (!u->is_SafePoint() || - (u->is_Call() && u->as_Call()->has_non_debug_use(n))) { - safe_to_skip = false; - } - } - } - if (safe_to_skip) { - n->set_req(j, in->in(1)); - } + if (in->is_DecodeNarrowPtr() && (is_uncommon || !in->has_non_debug_uses())) { + n->set_req(j, in->in(1)); if (in->outcnt() == 0) { in->disconnect_inputs(this); } diff --git a/src/hotspot/share/opto/compile.hpp b/src/hotspot/share/opto/compile.hpp index eb6be669f24..ff0085d79de 100644 --- a/src/hotspot/share/opto/compile.hpp +++ b/src/hotspot/share/opto/compile.hpp @@ -80,6 +80,7 @@ class PhaseIterGVN; class PhaseRegAlloc; class PhaseCCP; class PhaseOutput; +class ReachabilityFenceNode; class RootNode; class relocInfo; class StartNode; @@ -107,7 +108,8 @@ enum LoopOptsMode { LoopOptsMaxUnroll, LoopOptsShenandoahExpand, LoopOptsSkipSplitIf, - LoopOptsVerify + LoopOptsVerify, + PostLoopOptsExpandReachabilityFences }; // The type of all node counts and indexes. @@ -385,6 +387,7 @@ class Compile : public Phase { // of Template Assertion Predicates themselves. GrowableArray _template_assertion_predicate_opaques; GrowableArray _expensive_nodes; // List of nodes that are expensive to compute and that we'd better not let the GVN freely common + GrowableArray _reachability_fences; // List of reachability fences GrowableArray _for_post_loop_igvn; // List of nodes for IGVN after loop opts are over GrowableArray _for_merge_stores_igvn; // List of nodes for IGVN merge stores GrowableArray _unstable_if_traps; // List of ifnodes after IGVN @@ -714,11 +717,13 @@ public: int template_assertion_predicate_count() const { return _template_assertion_predicate_opaques.length(); } int expensive_count() const { return _expensive_nodes.length(); } int coarsened_count() const { return _coarsened_locks.length(); } - Node* macro_node(int idx) const { return _macro_nodes.at(idx); } Node* expensive_node(int idx) const { return _expensive_nodes.at(idx); } + ReachabilityFenceNode* reachability_fence(int idx) const { return _reachability_fences.at(idx); } + int reachability_fences_count() const { return _reachability_fences.length(); } + ConnectionGraph* congraph() { return _congraph;} void set_congraph(ConnectionGraph* congraph) { _congraph = congraph;} void add_macro_node(Node * n) { @@ -740,6 +745,14 @@ public: _expensive_nodes.remove_if_existing(n); } + void add_reachability_fence(ReachabilityFenceNode* rf) { + _reachability_fences.append(rf); + } + + void remove_reachability_fence(ReachabilityFenceNode* n) { + _reachability_fences.remove_if_existing(n); + } + void add_parse_predicate(ParsePredicateNode* n) { assert(!_parse_predicates.contains(n), "duplicate entry in Parse Predicate list"); _parse_predicates.append(n); @@ -1300,6 +1313,9 @@ public: // Definitions of pd methods static void pd_compiler2_init(); + // Materialize reachability fences from reachability edges on safepoints. + void expand_reachability_edges(Unique_Node_List& safepoints); + // Static parse-time type checking logic for gen_subtype_check: enum SubTypeCheckResult { SSC_always_false, SSC_always_true, SSC_easy_test, SSC_full_test }; SubTypeCheckResult static_subtype_check(const TypeKlassPtr* superk, const TypeKlassPtr* subk, bool skip = StressReflectiveCode); diff --git a/src/hotspot/share/opto/doCall.cpp b/src/hotspot/share/opto/doCall.cpp index e4418631d17..d6e75f17f50 100644 --- a/src/hotspot/share/opto/doCall.cpp +++ b/src/hotspot/share/opto/doCall.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, 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 @@ -37,6 +37,7 @@ #include "opto/callGenerator.hpp" #include "opto/castnode.hpp" #include "opto/cfgnode.hpp" +#include "opto/graphKit.hpp" #include "opto/mulnode.hpp" #include "opto/parse.hpp" #include "opto/rootnode.hpp" @@ -909,8 +910,7 @@ void Parse::catch_call_exceptions(ciExceptionHandlerStream& handlers) { if (handler_bci < 0) { // merge with corresponding rethrow node throw_to_exit(make_exception_state(ex_oop)); } else { // Else jump to corresponding handle - push_ex_oop(ex_oop); // Clear stack and push just the oop. - merge_exception(handler_bci); + push_and_merge_exception(handler_bci, ex_oop); } } @@ -1008,13 +1008,10 @@ void Parse::catch_inline_exceptions(SafePointNode* ex_map) { int handler_bci = handler->handler_bci(); if (remaining == 1) { - push_ex_oop(ex_node); // Push exception oop for handler if (PrintOpto && WizardMode) { tty->print_cr(" Catching every inline exception bci:%d -> handler_bci:%d", bci(), handler_bci); } - // If this is a backwards branch in the bytecodes, add safepoint - maybe_add_safepoint(handler_bci); - merge_exception(handler_bci); // jump to handler + push_and_merge_exception(handler_bci, ex_node); // jump to handler return; // No more handling to be done here! } @@ -1039,15 +1036,13 @@ void Parse::catch_inline_exceptions(SafePointNode* ex_map) { const TypeInstPtr* tinst = TypeOopPtr::make_from_klass_unique(klass)->cast_to_ptr_type(TypePtr::NotNull)->is_instptr(); assert(klass->has_subklass() || tinst->klass_is_exact(), "lost exactness"); Node* ex_oop = _gvn.transform(new CheckCastPPNode(control(), ex_node, tinst)); - push_ex_oop(ex_oop); // Push exception oop for handler if (PrintOpto && WizardMode) { tty->print(" Catching inline exception bci:%d -> handler_bci:%d -- ", bci(), handler_bci); klass->print_name(); tty->cr(); } // If this is a backwards branch in the bytecodes, add safepoint - maybe_add_safepoint(handler_bci); - merge_exception(handler_bci); + push_and_merge_exception(handler_bci, ex_oop); } set_control(not_subtype_ctrl); @@ -1086,13 +1081,13 @@ void Parse::catch_inline_exceptions(SafePointNode* ex_map) { #ifndef PRODUCT void Parse::count_compiled_calls(bool at_method_entry, bool is_inline) { - if( CountCompiledCalls ) { - if( at_method_entry ) { + if (CountCompiledCalls) { + if (at_method_entry) { // bump invocation counter if top method (for statistics) if (CountCompiledCalls && depth() == 1) { const TypePtr* addr_type = TypeMetadataPtr::make(method()); Node* adr1 = makecon(addr_type); - Node* adr2 = basic_plus_adr(adr1, adr1, in_bytes(Method::compiled_invocation_counter_offset())); + Node* adr2 = off_heap_plus_addr(adr1, in_bytes(Method::compiled_invocation_counter_offset())); increment_counter(adr2); } } else if (is_inline) { diff --git a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp index 7041eb0b810..a05ad0ef99a 100644 --- a/src/hotspot/share/opto/escape.cpp +++ b/src/hotspot/share/opto/escape.cpp @@ -933,7 +933,7 @@ void ConnectionGraph::reduce_phi_on_castpp_field_load(Node* curr_castpp, Growabl j = MIN2(j, (int)use->outcnt()-1); } - _igvn->remove_dead_node(use); + _igvn->remove_dead_node(use, PhaseIterGVN::NodeOrigin::Graph); } --i; i = MIN2(i, (int)curr_castpp->outcnt()-1); @@ -1273,21 +1273,33 @@ bool ConnectionGraph::reduce_phi_on_safepoints_helper(Node* ophi, Node* cast, No for (uint spi = 0; spi < safepoints.size(); spi++) { SafePointNode* sfpt = safepoints.at(spi)->as_SafePoint(); - JVMState *jvms = sfpt->jvms(); - uint merge_idx = (sfpt->req() - jvms->scloff()); - int debug_start = jvms->debug_start(); + + SafePointNode::NodeEdgeTempStorage non_debug_edges_worklist(*_igvn); + + // All sfpt inputs are implicitly included into debug info during the scalarization process below. + // Keep non-debug inputs separately, so they stay non-debug. + sfpt->remove_non_debug_edges(non_debug_edges_worklist); + + JVMState* jvms = sfpt->jvms(); + uint merge_idx = (sfpt->req() - jvms->scloff()); + int debug_start = jvms->debug_start(); SafePointScalarMergeNode* smerge = new SafePointScalarMergeNode(merge_t, merge_idx); smerge->init_req(0, _compile->root()); _igvn->register_new_node_with_optimizer(smerge); + assert(sfpt->jvms()->endoff() == sfpt->req(), "no extra edges past debug info allowed"); + // The next two inputs are: // (1) A copy of the original pointer to NSR objects. // (2) A selector, used to decide if we need to rematerialize an object // or use the pointer to a NSR object. - // See more details of these fields in the declaration of SafePointScalarMergeNode + // See more details of these fields in the declaration of SafePointScalarMergeNode. + // It is safe to include them into debug info straight away since create_scalarized_object_description() + // will include all newly added inputs into debug info anyway. sfpt->add_req(nsr_merge_pointer); sfpt->add_req(selector); + sfpt->jvms()->set_endoff(sfpt->req()); for (uint i = 1; i < ophi->req(); i++) { Node* base = ophi->in(i); @@ -1302,13 +1314,15 @@ bool ConnectionGraph::reduce_phi_on_safepoints_helper(Node* ophi, Node* cast, No AllocateNode* alloc = ptn->ideal_node()->as_Allocate(); SafePointScalarObjectNode* sobj = mexp.create_scalarized_object_description(alloc, sfpt); if (sobj == nullptr) { - return false; + sfpt->restore_non_debug_edges(non_debug_edges_worklist); + return false; // non-recoverable failure; recompile } // Now make a pass over the debug information replacing any references // to the allocated object with "sobj" Node* ccpp = alloc->result_cast(); sfpt->replace_edges_in_range(ccpp, sobj, debug_start, jvms->debug_end(), _igvn); + non_debug_edges_worklist.remove_edge_if_present(ccpp); // drop scalarized input from non-debug info // Register the scalarized object as a candidate for reallocation smerge->add_req(sobj); @@ -1316,11 +1330,15 @@ bool ConnectionGraph::reduce_phi_on_safepoints_helper(Node* ophi, Node* cast, No // Replaces debug information references to "original_sfpt_parent" in "sfpt" with references to "smerge" sfpt->replace_edges_in_range(original_sfpt_parent, smerge, debug_start, jvms->debug_end(), _igvn); + non_debug_edges_worklist.remove_edge_if_present(original_sfpt_parent); // drop scalarized input from non-debug info // The call to 'replace_edges_in_range' above might have removed the // reference to ophi that we need at _merge_pointer_idx. The line below make // sure the reference is maintained. sfpt->set_req(smerge->merge_pointer_idx(jvms), nsr_merge_pointer); + + sfpt->restore_non_debug_edges(non_debug_edges_worklist); + _igvn->_worklist.push(sfpt); } @@ -4712,6 +4730,7 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist, op == Op_StrIndexOf || op == Op_StrIndexOfChar || op == Op_SubTypeCheck || op == Op_ReinterpretS2HF || + op == Op_ReachabilityFence || BarrierSet::barrier_set()->barrier_set_c2()->is_gc_barrier_node(use))) { n->dump(); use->dump(); diff --git a/src/hotspot/share/opto/gcm.cpp b/src/hotspot/share/opto/gcm.cpp index 4a1553b1e00..ce1ea8a59e2 100644 --- a/src/hotspot/share/opto/gcm.cpp +++ b/src/hotspot/share/opto/gcm.cpp @@ -152,9 +152,12 @@ bool PhaseCFG::is_CFG(Node* n) { } bool PhaseCFG::is_control_proj_or_safepoint(Node* n) const { - bool result = (n->is_Mach() && n->as_Mach()->ideal_Opcode() == Op_SafePoint) || (n->is_Proj() && n->as_Proj()->bottom_type() == Type::CONTROL); - assert(!result || (n->is_Mach() && n->as_Mach()->ideal_Opcode() == Op_SafePoint) - || (n->is_Proj() && n->as_Proj()->_con == 0), "If control projection, it must be projection 0"); + bool result = n->is_ReachabilityFence() || + (n->is_Mach() && n->as_Mach()->ideal_Opcode() == Op_SafePoint) || + (n->is_Proj() && n->as_Proj()->bottom_type() == Type::CONTROL); + assert(!n->is_Proj() || + n->as_Proj()->bottom_type() != Type::CONTROL || + n->as_Proj()->_con == 0, "If control projection, it must be projection 0"); return result; } @@ -1740,6 +1743,9 @@ void PhaseCFG::schedule_late(VectorSet &visited, Node_Stack &stack) { // are needed make sure that after placement in a block we don't // need any new precedence edges. verify_anti_dependences(late, self); + if (C->failing()) { + return; + } } #endif } // Loop until all nodes have been visited diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp index e297292770e..bbd00d111f7 100644 --- a/src/hotspot/share/opto/graphKit.cpp +++ b/src/hotspot/share/opto/graphKit.cpp @@ -41,6 +41,7 @@ #include "opto/machnode.hpp" #include "opto/opaquenode.hpp" #include "opto/parse.hpp" +#include "opto/reachability.hpp" #include "opto/rootnode.hpp" #include "opto/runtime.hpp" #include "opto/subtypenode.hpp" @@ -2911,8 +2912,8 @@ Node* Phase::gen_subtype_check(Node* subklass, Node* superklass, Node** ctrl, No *ctrl = iftrue1; // We need exactly the 1 test above PhaseIterGVN* igvn = gvn.is_IterGVN(); if (igvn != nullptr) { - igvn->remove_globally_dead_node(r_ok_subtype); - igvn->remove_globally_dead_node(r_not_subtype); + igvn->remove_globally_dead_node(r_ok_subtype, PhaseIterGVN::NodeOrigin::Speculative); + igvn->remove_globally_dead_node(r_not_subtype, PhaseIterGVN::NodeOrigin::Speculative); } return not_subtype_ctrl; } @@ -3541,6 +3542,15 @@ Node* GraphKit::insert_mem_bar_volatile(int opcode, int alias_idx, Node* precede return membar; } +//------------------------------insert_reachability_fence---------------------- +Node* GraphKit::insert_reachability_fence(Node* referent) { + assert(!referent->is_top(), ""); + Node* rf = _gvn.transform(new ReachabilityFenceNode(C, control(), referent)); + set_control(rf); + C->record_for_igvn(rf); + return rf; +} + //------------------------------shared_lock------------------------------------ // Emit locking code. FastLockNode* GraphKit::shared_lock(Node* obj) { diff --git a/src/hotspot/share/opto/graphKit.hpp b/src/hotspot/share/opto/graphKit.hpp index 273009ca7ce..f53f73d0978 100644 --- a/src/hotspot/share/opto/graphKit.hpp +++ b/src/hotspot/share/opto/graphKit.hpp @@ -805,6 +805,7 @@ class GraphKit : public Phase { int next_monitor(); Node* insert_mem_bar(int opcode, Node* precedent = nullptr); Node* insert_mem_bar_volatile(int opcode, int alias_idx, Node* precedent = nullptr); + Node* insert_reachability_fence(Node* referent); // Optional 'precedent' is appended as an extra edge, to force ordering. FastLockNode* shared_lock(Node* obj); void shared_unlock(Node* box, Node* obj); diff --git a/src/hotspot/share/opto/ifnode.cpp b/src/hotspot/share/opto/ifnode.cpp index 762791d467d..ad8f0ced6ea 100644 --- a/src/hotspot/share/opto/ifnode.cpp +++ b/src/hotspot/share/opto/ifnode.cpp @@ -132,7 +132,7 @@ static Node* split_if(IfNode *iff, PhaseIterGVN *igvn) { cmp2->set_req(2,con2); const Type *t = cmp2->Value(igvn); // This compare is dead, so whack it! - igvn->remove_dead_node(cmp2); + igvn->remove_dead_node(cmp2, PhaseIterGVN::NodeOrigin::Speculative); if( !t->singleton() ) return nullptr; // No intervening control, like a simple Call @@ -443,7 +443,7 @@ static Node* split_if(IfNode *iff, PhaseIterGVN *igvn) { } l -= uses_found; // we deleted 1 or more copies of this edge } - igvn->remove_dead_node(p); + igvn->remove_dead_node(p, PhaseIterGVN::NodeOrigin::Graph); } // Force the original merge dead @@ -455,14 +455,14 @@ static Node* split_if(IfNode *iff, PhaseIterGVN *igvn) { r->set_req(0, nullptr); } else { assert(u->outcnt() == 0, "only dead users"); - igvn->remove_dead_node(u); + igvn->remove_dead_node(u, PhaseIterGVN::NodeOrigin::Graph); } l -= 1; } - igvn->remove_dead_node(r); + igvn->remove_dead_node(r, PhaseIterGVN::NodeOrigin::Graph); // Now remove the bogus extra edges used to keep things alive - igvn->remove_dead_node( hook ); + igvn->remove_dead_node(hook, PhaseIterGVN::NodeOrigin::Speculative); // Must return either the original node (now dead) or a new node // (Do not return a top here, since that would break the uniqueness of top.) @@ -905,6 +905,7 @@ bool IfNode::fold_compares_helper(IfProjNode* proj, IfProjNode* success, IfProjN IfNode* dom_iff = proj->in(0)->as_If(); BoolNode* dom_bool = dom_iff->in(1)->as_Bool(); Node* lo = dom_iff->in(1)->in(1)->in(2); + Node* orig_lo = lo; Node* hi = this_cmp->in(2); Node* n = this_cmp->in(1); IfProjNode* otherproj = proj->other_if_proj(); @@ -916,6 +917,7 @@ bool IfNode::fold_compares_helper(IfProjNode* proj, IfProjNode* success, IfProjN BoolTest::mask hi_test = this_bool->_test._test; BoolTest::mask cond = hi_test; + PhaseTransform::SpeculativeProgressGuard progress_guard(igvn); // convert: // // dom_bool = x {<,<=,>,>=} a @@ -1053,6 +1055,7 @@ bool IfNode::fold_compares_helper(IfProjNode* proj, IfProjNode* success, IfProjN // previous if determines the result of this if so // replace Bool with constant igvn->replace_input_of(this, 1, igvn->intcon(success->_con)); + progress_guard.commit(); return true; } } @@ -1087,11 +1090,14 @@ bool IfNode::fold_compares_helper(IfProjNode* proj, IfProjNode* success, IfProjN // min(limit, max(-2 + min_jint + 1, min_jint)) // = min(limit, min_jint) // = min_jint + if (lo != orig_lo && lo->outcnt() == 0) { + igvn->remove_dead_node(lo, PhaseIterGVN::NodeOrigin::Speculative); + } if (adjusted_val->outcnt() == 0) { - igvn->remove_dead_node(adjusted_val); + igvn->remove_dead_node(adjusted_val, PhaseIterGVN::NodeOrigin::Speculative); } if (adjusted_lim->outcnt() == 0) { - igvn->remove_dead_node(adjusted_lim); + igvn->remove_dead_node(adjusted_lim, PhaseIterGVN::NodeOrigin::Speculative); } igvn->C->record_for_post_loop_opts_igvn(this); return false; @@ -1103,6 +1109,7 @@ bool IfNode::fold_compares_helper(IfProjNode* proj, IfProjNode* success, IfProjN igvn->replace_input_of(dom_iff, 1, igvn->intcon(proj->_con)); igvn->replace_input_of(this, 1, newbool); + progress_guard.commit(); return true; } @@ -1592,11 +1599,11 @@ Node* IfNode::dominated_by(Node* prev_dom, PhaseIterGVN* igvn, bool prev_dom_not } } // End for each child of a projection - igvn->remove_dead_node(ifp); + igvn->remove_dead_node(ifp, PhaseIterGVN::NodeOrigin::Graph); } // End for each IfTrue/IfFalse child of If // Kill the IfNode - igvn->remove_dead_node(this); + igvn->remove_dead_node(this, PhaseIterGVN::NodeOrigin::Graph); // Must return either the original node (now dead) or a new node // (Do not return a top here, since that would break the uniqueness of top.) @@ -1758,7 +1765,7 @@ Node* IfNode::simple_subsuming(PhaseIterGVN* igvn) { } if (bol->outcnt() == 0) { - igvn->remove_dead_node(bol); // Kill the BoolNode. + igvn->remove_dead_node(bol, PhaseIterGVN::NodeOrigin::Graph); // Kill the BoolNode. } return this; } @@ -1903,7 +1910,7 @@ static IfNode* idealize_test(PhaseGVN* phase, IfNode* iff) { Node *prior = igvn->hash_find_insert(iff); if( prior ) { - igvn->remove_dead_node(iff); + igvn->remove_dead_node(iff, PhaseIterGVN::NodeOrigin::Graph); iff = (IfNode*)prior; } else { // Cannot call transform on it just yet diff --git a/src/hotspot/share/opto/lcm.cpp b/src/hotspot/share/opto/lcm.cpp index 53a503866fa..450d267e821 100644 --- a/src/hotspot/share/opto/lcm.cpp +++ b/src/hotspot/share/opto/lcm.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, 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 @@ -306,8 +306,7 @@ void PhaseCFG::implicit_null_check(Block* block, Node *proj, Node *val, int allo // cannot reason about it; is probably not implicit null exception } else { const TypePtr* tptr; - if ((UseCompressedOops && CompressedOops::shift() == 0) || - (UseCompressedClassPointers && CompressedKlassPointers::shift() == 0)) { + if ((UseCompressedOops && CompressedOops::shift() == 0) || CompressedKlassPointers::shift() == 0) { // 32-bits narrow oop can be the base of address expressions tptr = base->get_ptr_type(); } else { diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index eed99ceb8bc..ff7bc2c10d3 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -568,6 +568,7 @@ bool LibraryCallKit::try_to_inline(int predicate) { case vmIntrinsics::_Reference_get0: return inline_reference_get0(); case vmIntrinsics::_Reference_refersTo0: return inline_reference_refersTo0(false); + case vmIntrinsics::_Reference_reachabilityFence: return inline_reference_reachabilityFence(); case vmIntrinsics::_PhantomReference_refersTo0: return inline_reference_refersTo0(true); case vmIntrinsics::_Reference_clear0: return inline_reference_clear0(false); case vmIntrinsics::_PhantomReference_clear0: return inline_reference_clear0(true); @@ -1819,61 +1820,17 @@ bool LibraryCallKit::runtime_math(const TypeFunc* call_type, address funcAddr, c //------------------------------inline_math_pow----------------------------- bool LibraryCallKit::inline_math_pow() { + Node* base = argument(0); Node* exp = argument(2); - const TypeD* d = _gvn.type(exp)->isa_double_constant(); - if (d != nullptr) { - if (d->getd() == 2.0) { - // Special case: pow(x, 2.0) => x * x - Node* base = argument(0); - set_result(_gvn.transform(new MulDNode(base, base))); - return true; - } else if (d->getd() == 0.5 && Matcher::match_rule_supported(Op_SqrtD)) { - // Special case: pow(x, 0.5) => sqrt(x) - Node* base = argument(0); - Node* zero = _gvn.zerocon(T_DOUBLE); - RegionNode* region = new RegionNode(3); - Node* phi = new PhiNode(region, Type::DOUBLE); - - Node* cmp = _gvn.transform(new CmpDNode(base, zero)); - // According to the API specs, pow(-0.0, 0.5) = 0.0 and sqrt(-0.0) = -0.0. - // So pow(-0.0, 0.5) shouldn't be replaced with sqrt(-0.0). - // -0.0/+0.0 are both excluded since floating-point comparison doesn't distinguish -0.0 from +0.0. - Node* test = _gvn.transform(new BoolNode(cmp, BoolTest::le)); - - Node* if_pow = generate_slow_guard(test, nullptr); - Node* value_sqrt = _gvn.transform(new SqrtDNode(C, control(), base)); - phi->init_req(1, value_sqrt); - region->init_req(1, control()); - - if (if_pow != nullptr) { - set_control(if_pow); - address target = StubRoutines::dpow() != nullptr ? StubRoutines::dpow() : - CAST_FROM_FN_PTR(address, SharedRuntime::dpow); - const TypePtr* no_memory_effects = nullptr; - Node* trig = make_runtime_call(RC_LEAF, OptoRuntime::Math_DD_D_Type(), target, "POW", - no_memory_effects, base, top(), exp, top()); - Node* value_pow = _gvn.transform(new ProjNode(trig, TypeFunc::Parms+0)); -#ifdef ASSERT - Node* value_top = _gvn.transform(new ProjNode(trig, TypeFunc::Parms+1)); - assert(value_top == top(), "second value must be top"); -#endif - phi->init_req(2, value_pow); - region->init_req(2, _gvn.transform(new ProjNode(trig, TypeFunc::Control))); - } - - C->set_has_split_ifs(true); // Has chance for split-if optimization - set_control(_gvn.transform(region)); - record_for_igvn(region); - set_result(_gvn.transform(phi)); - - return true; - } - } - - return StubRoutines::dpow() != nullptr ? - runtime_math(OptoRuntime::Math_DD_D_Type(), StubRoutines::dpow(), "dpow") : - runtime_math(OptoRuntime::Math_DD_D_Type(), CAST_FROM_FN_PTR(address, SharedRuntime::dpow), "POW"); + CallNode* pow = new PowDNode(C, base, exp); + set_predefined_input_for_runtime_call(pow); + pow = _gvn.transform(pow)->as_CallLeafPure(); + set_predefined_output_for_runtime_call(pow); + Node* result = _gvn.transform(new ProjNode(pow, TypeFunc::Parms + 0)); + record_for_igvn(pow); + set_result(result); + return true; } //------------------------------inline_math_native----------------------------- @@ -4354,7 +4311,7 @@ Node* LibraryCallKit::generate_array_guard_common(Node* kls, RegionNode* region, if (obj != nullptr && is_array_ctrl != nullptr && is_array_ctrl != top()) { // Keep track of the fact that 'obj' is an array to prevent // array specific accesses from floating above the guard. - *obj = _gvn.transform(new CastPPNode(is_array_ctrl, *obj, TypeAryPtr::BOTTOM)); + *obj = _gvn.transform(new CheckCastPPNode(is_array_ctrl, *obj, TypeAryPtr::BOTTOM)); } return ctrl; } @@ -7069,6 +7026,14 @@ bool LibraryCallKit::inline_reference_clear0(bool is_phantom) { return true; } +//-----------------------inline_reference_reachabilityFence----------------- +// bool java.lang.ref.Reference.reachabilityFence(); +bool LibraryCallKit::inline_reference_reachabilityFence() { + Node* referent = argument(0); + insert_reachability_fence(referent); + return true; +} + Node* LibraryCallKit::load_field_from_object(Node* fromObj, const char* fieldName, const char* fieldTypeString, DecoratorSet decorators, bool is_static, ciInstanceKlass* fromKls) { diff --git a/src/hotspot/share/opto/library_call.hpp b/src/hotspot/share/opto/library_call.hpp index 9b87df645e1..9aae48302cf 100644 --- a/src/hotspot/share/opto/library_call.hpp +++ b/src/hotspot/share/opto/library_call.hpp @@ -312,6 +312,7 @@ class LibraryCallKit : public GraphKit { bool inline_divmod_methods(vmIntrinsics::ID id); bool inline_reference_get0(); bool inline_reference_refersTo0(bool is_phantom); + bool inline_reference_reachabilityFence(); bool inline_reference_clear0(bool is_phantom); bool inline_Class_cast(); bool inline_aescrypt_Block(vmIntrinsics::ID id); diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp index 80b17efb998..b65f90093ab 100644 --- a/src/hotspot/share/opto/loopTransform.cpp +++ b/src/hotspot/share/opto/loopTransform.cpp @@ -35,7 +35,9 @@ #include "opto/loopnode.hpp" #include "opto/movenode.hpp" #include "opto/mulnode.hpp" +#include "opto/node.hpp" #include "opto/opaquenode.hpp" +#include "opto/opcodes.hpp" #include "opto/phase.hpp" #include "opto/predicates.hpp" #include "opto/rootnode.hpp" @@ -50,17 +52,70 @@ // Given an IfNode, return the loop-exiting projection or null if both // arms remain in the loop. Node *IdealLoopTree::is_loop_exit(Node *iff) const { - if (iff->outcnt() != 2) return nullptr; // Ignore partially dead tests - PhaseIdealLoop *phase = _phase; + assert(iff->is_If(), "not an If: %s", iff->Name()); + assert(is_member(_phase->get_loop(iff)), "not related"); + + if (iff->outcnt() != 2) { + return nullptr; // Ignore partially dead tests + } // Test is an IfNode, has 2 projections. If BOTH are in the loop // we need loop unswitching instead of peeling. - if (!is_member(phase->get_loop(iff->raw_out(0)))) + if (!is_member(_phase->get_loop(iff->raw_out(0)))) { return iff->raw_out(0); - if (!is_member(phase->get_loop(iff->raw_out(1)))) + } + if (!is_member(_phase->get_loop(iff->raw_out(1)))) { return iff->raw_out(1); + } return nullptr; } +//------------------------------unique_loop_exit_or_null---------------------- +// Return the loop-exit projection if loop exit is unique. +IfFalseNode* IdealLoopTree::unique_loop_exit_proj_or_null() { + if (is_loop() && head()->is_BaseCountedLoop()) { + IfNode* loop_end = head()->as_BaseCountedLoop()->loopexit_or_null(); + if (loop_end == nullptr) { + return nullptr; // malformed loop shape + } + // Look for other loop exits. + assert(_phase->is_dominator(head(), tail()), "sanity"); + for (Node* ctrl = tail(); ctrl != head(); ctrl = ctrl->in(0)) { + assert(is_member(_phase->get_loop(ctrl)), "sanity"); + if (ctrl->is_If()) { + if (!is_loop_exit(ctrl->as_If())) { + continue; // local branch + } else if (ctrl != loop_end) { + return nullptr; // multiple loop exits + } + } else if (ctrl->is_Region()) { + return nullptr; // give up on control flow merges + } else if (ctrl->is_ReachabilityFence() || + ctrl->is_SafePoint() || + ctrl->is_MemBar() || + ctrl->Opcode() == Op_Blackhole) { + continue; // skip + } else if (ctrl->is_Proj()) { + if (ctrl->is_IfProj() || + ctrl->Opcode() == Op_SCMemProj || + ctrl->Opcode() == Op_Proj) { + continue; // skip simple control projections + } else if (ctrl->is_CatchProj() || + ctrl->is_JumpProj()) { + return nullptr; // give up on control flow splits + } else { + assert(false, "unknown control projection: %s", ctrl->Name()); + return nullptr; // stop on unknown control node + } + } else { + assert(false, "unknown CFG node: %s", ctrl->Name()); + return nullptr; // stop on unknown control node + } + } + assert(is_loop_exit(loop_end), "not a loop exit?"); + return loop_end->false_proj_or_null(); + } + return nullptr; // not found or multiple loop exits +} //============================================================================= @@ -1774,13 +1829,39 @@ Node *PhaseIdealLoop::insert_post_loop(IdealLoopTree* loop, Node_List& old_new, for (DUIterator i = main_head->outs(); main_head->has_out(i); i++) { Node* main_phi = main_head->out(i); if (main_phi->is_Phi() && main_phi->in(0) == main_head && main_phi->outcnt() > 0) { - Node* cur_phi = old_new[main_phi->_idx]; + Node* post_phi = old_new[main_phi->_idx]; + Node* loopback_input = main_phi->in(LoopNode::LoopBackControl); Node* fallnew = clone_up_backedge_goo(main_head->back_control(), post_head->init_control(), - main_phi->in(LoopNode::LoopBackControl), + loopback_input, visited, clones); - _igvn.hash_delete(cur_phi); - cur_phi->set_req(LoopNode::EntryControl, fallnew); + // Technically, the entry value of post_phi must be the loop back input of the corresponding + // Phi of the outer loop, not the Phi of the inner loop (i.e. main_phi). However, we have not + // constructed the Phis for the OuterStripMinedLoop yet, so the input must be inferred from + // the loop back input of main_phi. + // - If post_phi is a data Phi, then we can use the loop back input of main_phi. + // - If post_phi is a memory Phi, since Stores can be sunk below the inner loop, but still + // inside the outer loop, we have 2 cases: + // + If the loop back input of main_phi is on the backedge, then the entry input of + // post_phi is the clone of the node on the entry of post_head, similar to when post_phi + // is a data Phi. + // + If the loop back input of main_phi is not on the backedge, we need to find whether + // there is a sunk Store corresponding to post_phi, if there is any, the latest such + // store will be the entry input of post_phi. Fortunately, the safepoint at the exit of + // the outer loop captures all memory states, so we can use it as the entry input of + // post_phi. + // Another way to see it is that, the memory phi should capture the latest state at the + // post-loop entry. If loopback_input is cloned by clone_up_backedge_goo, it is pinned at + // the post-loop entry, and is surely the latest state. Otherwise, the latest memory state + // corresponding to post_phi is the memory state at the exit of the outer main-loop, which + // is captured by the safepoint there. + if (main_head->is_strip_mined() && fallnew == loopback_input && post_phi->is_memory_phi()) { + SafePointNode* main_safepoint = main_head->outer_safepoint(); + assert(main_safepoint != nullptr, "outer loop must have a safepoint"); + fallnew = main_safepoint->memory(); + } + _igvn.hash_delete(post_phi); + post_phi->set_req(LoopNode::EntryControl, fallnew); } } // Store nodes that were moved to the outer loop by PhaseIdealLoop::try_move_store_after_loop @@ -3107,9 +3188,13 @@ static CountedLoopNode* locate_pre_from_main(CountedLoopNode* main_loop) { Node* ctrl = main_loop->skip_assertion_predicates_with_halt(); assert(ctrl->Opcode() == Op_IfTrue || ctrl->Opcode() == Op_IfFalse, ""); Node* iffm = ctrl->in(0); - assert(iffm->Opcode() == Op_If, ""); + assert(iffm->Opcode() == Op_If, "%s", iffm->Name()); Node* p_f = iffm->in(0); - assert(p_f->Opcode() == Op_IfFalse, ""); + // Skip ReachabilityFences hoisted out of pre-loop. + while (p_f->is_ReachabilityFence()) { + p_f = p_f->in(0); + } + assert(p_f->Opcode() == Op_IfFalse, "%s", p_f->Name()); CountedLoopNode* pre_loop = p_f->in(0)->as_CountedLoopEnd()->loopnode(); assert(pre_loop->is_pre_loop(), "No pre loop found"); return pre_loop; diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index b2eb0c47458..13feeb77947 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -44,6 +44,7 @@ #include "opto/opaquenode.hpp" #include "opto/opcodes.hpp" #include "opto/predicates.hpp" +#include "opto/reachability.hpp" #include "opto/rootnode.hpp" #include "opto/runtime.hpp" #include "opto/vectorization.hpp" @@ -697,7 +698,7 @@ SafePointNode* PhaseIdealLoop::find_safepoint(Node* back_control, const Node* he } #ifdef ASSERT if (mm != nullptr) { - _igvn.remove_dead_node(mm); + _igvn.remove_dead_node(mm, PhaseIterGVN::NodeOrigin::Speculative); } #endif } @@ -2002,7 +2003,7 @@ bool CountedLoopConverter::stress_long_counted_loop() { Node* n = iv_nodes.at(i); Node* clone = old_new[n->_idx]; if (clone != nullptr) { - igvn->remove_dead_node(clone); + igvn->remove_dead_node(clone, PhaseIterGVN::NodeOrigin::Speculative); } } return false; @@ -2137,7 +2138,6 @@ bool CountedLoopConverter::is_counted_loop() { } assert(_head->Opcode() == Op_Loop || _head->Opcode() == Op_LongCountedLoop, "regular loops only"); - _phase->C->print_method(PHASE_BEFORE_CLOOPS, 3, _head); // =================================================== // We can only convert this loop to a counted loop if we can guarantee that the iv phi will never overflow at runtime. @@ -2411,6 +2411,12 @@ bool CountedLoopConverter::is_counted_loop() { _checked_for_counted_loop = true; #endif +#ifndef PRODUCT + if (StressCountedLoop && (_phase->C->random() % 2 == 0)) { + return false; + } +#endif + return true; } @@ -2553,6 +2559,8 @@ IdealLoopTree* CountedLoopConverter::convert() { PhaseIterGVN* igvn = &_phase->igvn(); + _phase->C->print_method(PHASE_BEFORE_CLOOPS, 3, _head); + if (_should_insert_stride_overflow_limit_check) { insert_stride_overflow_limit_check(); } @@ -3927,6 +3935,7 @@ IdealLoopTree::IdealLoopTree(PhaseIdealLoop* phase, Node* head, Node* tail): _pa _has_range_checks(0), _has_range_checks_computed(0), _safepts(nullptr), _required_safept(nullptr), + _reachability_fences(nullptr), _allow_optimizations(true) { precond(_head != nullptr); precond(_tail != nullptr); @@ -4338,8 +4347,13 @@ void IdealLoopTree::allpaths_check_safepts(VectorSet &visited, Node_List &stack) visited.set(_head->_idx); while (stack.size() > 0) { Node* n = stack.pop(); - if (n->is_Call() && n->as_Call()->guaranteed_safepoint()) { - // Terminate this path + if (n->is_Call() && n->as_Call()->guaranteed_safepoint() + && !(n->is_CallStaticJava() && n->as_CallStaticJava()->is_boxing_method())) { + // Terminate this path: guaranteed safepoint found. + // Boxing CallStaticJava calls are excluded as they may lack a safepoint on the fast path. This is + // not done via CallStaticJavaNode::guaranteed_safepoint() as that also controls PcDesc emission. + // In the future, guaranteed_safepoint() should be reworked to correctly handle boxing methods + // to avoid this additional check. } else if (n->Opcode() == Op_SafePoint) { if (_phase->get_loop(n) != this) { if (_required_safept == nullptr) _required_safept = new Node_List(); @@ -4437,7 +4451,12 @@ void IdealLoopTree::check_safepts(VectorSet &visited, Node_List &stack) { if (!_irreducible) { // Scan the dom-path nodes from tail to head for (Node* n = tail(); n != _head; n = _phase->idom(n)) { - if (n->is_Call() && n->as_Call()->guaranteed_safepoint()) { + // Boxing CallStaticJava calls are excluded as they may lack a safepoint on the fast path. This is + // not done via CallStaticJavaNode::guaranteed_safepoint() as that also controls PcDesc emission. + // In the future, guaranteed_safepoint() should be reworked to correctly handle boxing methods + // to avoid this additional check. + if (n->is_Call() && n->as_Call()->guaranteed_safepoint() + && !(n->is_CallStaticJava() && n->as_CallStaticJava()->is_boxing_method())) { has_call = true; _has_sfpt = 1; // Then no need for a safept! break; @@ -4659,7 +4678,7 @@ void PhaseIdealLoop::replace_parallel_iv(IdealLoopTree *loop) { _igvn.replace_node( phi2, add ); // Sometimes an induction variable is unused if (add->outcnt() == 0) { - _igvn.remove_dead_node(add); + _igvn.remove_dead_node(add, PhaseIterGVN::NodeOrigin::Graph); } --i; // deleted this phi; rescan starting with next position } @@ -4734,7 +4753,11 @@ void IdealLoopTree::counted_loop( PhaseIdealLoop *phase ) { } else if (_head->is_LongCountedLoop() || phase->try_convert_to_counted_loop(_head, loop, T_LONG)) { remove_safepoints(phase, true); } else { - assert(!_head->is_Loop() || !_head->as_Loop()->is_loop_nest_inner_loop(), "transformation to counted loop should not fail"); + // When StressCountedLoop is enabled, this loop may intentionally avoid a counted loop conversion. + // This is expected behavior for the stress mode, which exercises alternative compilation paths. + if (!StressCountedLoop) { + assert(!_head->is_Loop() || !_head->as_Loop()->is_loop_nest_inner_loop(), "transformation to counted loop should not fail"); + } if (_parent != nullptr && !_irreducible) { // Not a counted loop. Keep one safepoint. bool keep_one_sfpt = true; @@ -4834,6 +4857,15 @@ uint IdealLoopTree::est_loop_flow_merge_sz() const { return 0; } +void IdealLoopTree::register_reachability_fence(ReachabilityFenceNode* rf) { + if (_reachability_fences == nullptr) { + _reachability_fences = new Node_List(); + } + if (!_reachability_fences->contains(rf)) { + _reachability_fences->push(rf); + } +} + #ifndef PRODUCT //------------------------------dump_head-------------------------------------- // Dump 1 liner for loop header info @@ -4893,6 +4925,9 @@ void IdealLoopTree::dump_head() { if (_has_call) tty->print(" has_call"); if (_has_sfpt) tty->print(" has_sfpt"); if (_rce_candidate) tty->print(" rce"); + if (_reachability_fences != nullptr && _reachability_fences->size() > 0) { + tty->print(" has_rf"); + } if (_safepts != nullptr && _safepts->size() > 0) { tty->print(" sfpts={"); _safepts->dump_simple(); tty->print(" }"); } @@ -4900,6 +4935,9 @@ void IdealLoopTree::dump_head() { tty->print(" req={"); _required_safept->dump_simple(); tty->print(" }"); } if (Verbose) { + if (_reachability_fences != nullptr && _reachability_fences->size() > 0) { + tty->print(" rfs={"); _reachability_fences->dump_simple(); tty->print(" }"); + } tty->print(" body={"); _body.dump_simple(); tty->print(" }"); } if (_head->is_Loop() && _head->as_Loop()->is_strip_mined()) { @@ -5158,12 +5196,14 @@ bool PhaseIdealLoop::process_expensive_nodes() { // Create a PhaseLoop. Build the ideal Loop tree. Map each Ideal Node to // its corresponding LoopNode. If 'optimize' is true, do some loop cleanups. void PhaseIdealLoop::build_and_optimize() { - assert(!C->post_loop_opts_phase(), "no loop opts allowed"); - bool do_split_ifs = (_mode == LoopOptsDefault); bool skip_loop_opts = (_mode == LoopOptsNone); bool do_max_unroll = (_mode == LoopOptsMaxUnroll); + bool do_verify = (_mode == LoopOptsVerify); + bool do_expand_reachability_fences = (_mode == PostLoopOptsExpandReachabilityFences); + assert(!C->post_loop_opts_phase() || do_expand_reachability_fences || do_verify, + "no loop opts allowed"); bool old_progress = C->major_progress(); uint orig_worklist_size = _igvn._worklist.size(); @@ -5230,11 +5270,13 @@ void PhaseIdealLoop::build_and_optimize() { BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2(); // Nothing to do, so get out - bool stop_early = !C->has_loops() && !skip_loop_opts && !do_split_ifs && !do_max_unroll && !_verify_me && - !_verify_only && !bs->is_gc_specific_loop_opts_pass(_mode); + bool stop_early = !C->has_loops() && !skip_loop_opts && !do_split_ifs && !do_max_unroll && + !do_expand_reachability_fences && !_verify_me && !_verify_only && + !bs->is_gc_specific_loop_opts_pass(_mode) ; bool do_expensive_nodes = C->should_optimize_expensive_nodes(_igvn); + bool do_optimize_reachability_fences = OptimizeReachabilityFences && (C->reachability_fences_count() > 0); bool strip_mined_loops_expanded = bs->strip_mined_loops_expanded(_mode); - if (stop_early && !do_expensive_nodes) { + if (stop_early && !do_expensive_nodes && !do_optimize_reachability_fences) { return; } @@ -5310,7 +5352,7 @@ void PhaseIdealLoop::build_and_optimize() { // Given early legal placement, try finding counted loops. This placement // is good enough to discover most loop invariants. - if (!_verify_me && !_verify_only && !strip_mined_loops_expanded) { + if (!_verify_me && !_verify_only && !strip_mined_loops_expanded && !do_expand_reachability_fences) { _ltree_root->counted_loop( this ); } @@ -5333,15 +5375,21 @@ void PhaseIdealLoop::build_and_optimize() { // clear out the dead code after build_loop_late while (_deadlist.size()) { - _igvn.remove_globally_dead_node(_deadlist.pop()); + _igvn.remove_globally_dead_node(_deadlist.pop(), PhaseIterGVN::NodeOrigin::Graph); } eliminate_useless_zero_trip_guard(); eliminate_useless_multiversion_if(); if (stop_early) { - assert(do_expensive_nodes, "why are we here?"); - if (process_expensive_nodes()) { + assert(do_expensive_nodes || do_optimize_reachability_fences, "why are we here?"); + // Use the opportunity to optimize reachability fence nodes irrespective of + // whether loop optimizations are performed or not. + if (do_optimize_reachability_fences && optimize_reachability_fences()) { + recompute_dom_depth(); + DEBUG_ONLY( if (VerifyLoopOptimizations) { verify(); } ); + } + if (do_expensive_nodes && process_expensive_nodes()) { // If we made some progress when processing expensive nodes then // the IGVN may modify the graph in a way that will allow us to // make some more progress: we need to try processing expensive @@ -5369,6 +5417,22 @@ void PhaseIdealLoop::build_and_optimize() { } #endif + if (do_optimize_reachability_fences && optimize_reachability_fences()) { + recompute_dom_depth(); + DEBUG_ONLY( if (VerifyLoopOptimizations) { verify(); } ); + } + + if (do_expand_reachability_fences) { + assert(C->post_loop_opts_phase(), "required"); + if (expand_reachability_fences()) { + recompute_dom_depth(); + DEBUG_ONLY( if (VerifyLoopOptimizations) { verify(); } ); + } + return; + } + + assert(!C->post_loop_opts_phase(), "required"); + if (skip_loop_opts) { C->restore_major_progress(old_progress); return; @@ -5874,8 +5938,8 @@ void PhaseIdealLoop::set_idom(Node* d, Node* n, uint dom_depth) { uint idx = d->_idx; if (idx >= _idom_size) { uint newsize = next_power_of_2(idx); - _idom = REALLOC_ARENA_ARRAY(&_arena, Node*, _idom,_idom_size,newsize); - _dom_depth = REALLOC_ARENA_ARRAY(&_arena, uint, _dom_depth,_idom_size,newsize); + _idom = REALLOC_ARENA_ARRAY(&_arena, _idom, _idom_size, newsize); + _dom_depth = REALLOC_ARENA_ARRAY(&_arena, _dom_depth, _idom_size, newsize); memset( _dom_depth + _idom_size, 0, (newsize - _idom_size) * sizeof(uint) ); _idom_size = newsize; } @@ -6265,6 +6329,8 @@ int PhaseIdealLoop::build_loop_tree_impl(Node* n, int pre_order) { // Record all safepoints in this loop. if (innermost->_safepts == nullptr) innermost->_safepts = new Node_List(); innermost->_safepts->push(n); + } else if (n->is_ReachabilityFence()) { + innermost->register_reachability_fence(n->as_ReachabilityFence()); } } } diff --git a/src/hotspot/share/opto/loopnode.hpp b/src/hotspot/share/opto/loopnode.hpp index 6667c71511c..4eef2fed415 100644 --- a/src/hotspot/share/opto/loopnode.hpp +++ b/src/hotspot/share/opto/loopnode.hpp @@ -44,6 +44,7 @@ class PredicateBlock; class PathFrequency; class PhaseIdealLoop; class LoopSelector; +class ReachabilityFenceNode; class UnswitchedLoopSelector; class VectorSet; class VSharedData; @@ -662,6 +663,7 @@ public: Node_List* _safepts; // List of safepoints in this loop Node_List* _required_safept; // A inner loop cannot delete these safepts; + Node_List* _reachability_fences; // List of reachability fences in this loop bool _allow_optimizations; // Allow loop optimizations IdealLoopTree(PhaseIdealLoop* phase, Node* head, Node* tail); @@ -720,6 +722,9 @@ public: // Check for Node being a loop-breaking test Node *is_loop_exit(Node *iff) const; + // Return unique loop-exit projection or null if the loop has multiple exits. + IfFalseNode* unique_loop_exit_proj_or_null(); + // Remove simplistic dead code from loop body void DCE_loop_body(); @@ -825,6 +830,9 @@ public: return _head->as_Loop()->is_strip_mined() ? _parent : this; } + // Registers a reachability fence node in the loop. + void register_reachability_fence(ReachabilityFenceNode* rf); + #ifndef PRODUCT void dump_head(); // Dump loop head only void dump(); // Dump this loop recursively @@ -906,7 +914,7 @@ class PhaseIdealLoop : public PhaseTransform { void reallocate_preorders() { _nesting.check(); // Check if a potential re-allocation in the resource arena is safe if ( _max_preorder < C->unique() ) { - _preorders = REALLOC_RESOURCE_ARRAY(uint, _preorders, _max_preorder, C->unique()); + _preorders = REALLOC_RESOURCE_ARRAY(_preorders, _max_preorder, C->unique()); _max_preorder = C->unique(); } memset(_preorders, 0, sizeof(uint) * _max_preorder); @@ -918,7 +926,7 @@ class PhaseIdealLoop : public PhaseTransform { _nesting.check(); // Check if a potential re-allocation in the resource arena is safe if ( _max_preorder < C->unique() ) { uint newsize = _max_preorder<<1; // double size of array - _preorders = REALLOC_RESOURCE_ARRAY(uint, _preorders, _max_preorder, newsize); + _preorders = REALLOC_RESOURCE_ARRAY(_preorders, _max_preorder, newsize); memset(&_preorders[_max_preorder],0,sizeof(uint)*(newsize-_max_preorder)); _max_preorder = newsize; } @@ -1167,6 +1175,16 @@ public: forward_ctrl(old_node, new_node); } + void remove_dead_data_node(Node* dead) { + assert(dead->outcnt() == 0 && !dead->is_top(), "must be dead"); + assert(!dead->is_CFG(), "not a data node"); + Node* c = get_ctrl(dead); + IdealLoopTree* lpt = get_loop(c); + _loop_or_ctrl.map(dead->_idx, nullptr); // This node is useless + lpt->_body.yank(dead); + igvn().remove_dead_node(dead, PhaseIterGVN::NodeOrigin::Graph); + } + private: // Place 'n' in some loop nest, where 'n' is a CFG node @@ -1599,6 +1617,15 @@ public: // Implementation of the loop predication to promote checks outside the loop bool loop_predication_impl(IdealLoopTree *loop); + // Reachability Fence (RF) support. + private: + void insert_rf(Node* ctrl, Node* referent); + void replace_rf(Node* old_node, Node* new_node); + void remove_rf(ReachabilityFenceNode* rf); + public: + bool optimize_reachability_fences(); + bool expand_reachability_fences(); + private: bool loop_predication_impl_helper(IdealLoopTree* loop, IfProjNode* if_success_proj, ParsePredicateSuccessProj* parse_predicate_proj, CountedLoopNode* cl, ConNode* zero, diff --git a/src/hotspot/share/opto/loopopts.cpp b/src/hotspot/share/opto/loopopts.cpp index 4e447edee8d..ccd53129a87 100644 --- a/src/hotspot/share/opto/loopopts.cpp +++ b/src/hotspot/share/opto/loopopts.cpp @@ -161,7 +161,7 @@ Node* PhaseIdealLoop::split_thru_phi(Node* n, Node* region, int policy) { } if (the_clone != x) { - _igvn.remove_dead_node(the_clone); + _igvn.remove_dead_node(the_clone, PhaseIterGVN::NodeOrigin::Speculative); } else if (region->is_Loop() && i == LoopNode::LoopBackControl && n->is_Load() && can_move_to_inner_loop(n, region->as_Loop(), x)) { // it is not a win if 'x' moved from an outer to an inner loop @@ -172,7 +172,7 @@ Node* PhaseIdealLoop::split_thru_phi(Node* n, Node* region, int policy) { } // Too few wins? if (!wins.profitable(policy)) { - _igvn.remove_dead_node(phi); + _igvn.remove_dead_node(phi, PhaseIterGVN::NodeOrigin::Speculative); return nullptr; } @@ -1307,7 +1307,14 @@ Node* PhaseIdealLoop::place_outside_loop(Node* useblock, IdealLoopTree* loop) co bool PhaseIdealLoop::identical_backtoback_ifs(Node *n) { - if (!n->is_If() || n->is_BaseCountedLoopEnd()) { + if (!n->is_If()) { + return false; + } + if (n->outcnt() != n->as_If()->required_outcnt()) { + assert(false, "malformed IfNode with %d outputs", n->outcnt()); + return false; + } + if (n->is_BaseCountedLoopEnd()) { return false; } if (!n->in(0)->is_Region()) { @@ -1433,7 +1440,10 @@ void PhaseIdealLoop::split_if_with_blocks_post(Node *n) { // Check some safety conditions if (iff->is_If()) { // Classic split-if? - if (iff->in(0) != n_ctrl) { + if (iff->outcnt() != iff->as_If()->required_outcnt()) { + assert(false, "malformed IfNode with %d outputs", iff->outcnt()); + return; + } else if (iff->in(0) != n_ctrl) { return; // Compare must be in same blk as if } } else if (iff->is_CMove()) { // Trying to split-up a CMOVE @@ -1866,7 +1876,7 @@ void PhaseIdealLoop::try_sink_out_of_loop(Node* n) { assert(cast != nullptr, "must have added a cast to pin the node"); } } - _igvn.remove_dead_node(n); + _igvn.remove_dead_node(n, PhaseIterGVN::NodeOrigin::Graph); } _dom_lca_tags_round = 0; } @@ -2082,7 +2092,7 @@ Node* PhaseIdealLoop::clone_iff(PhiNode* phi) { // Register with optimizer Node *hit1 = _igvn.hash_find_insert(phi1); if (hit1) { // Hit, toss just made Phi - _igvn.remove_dead_node(phi1); // Remove new phi + _igvn.remove_dead_node(phi1, PhaseIterGVN::NodeOrigin::Speculative); // Remove new phi assert(hit1->is_Phi(), "" ); phi1 = (PhiNode*)hit1; // Use existing phi } else { // Miss @@ -2090,7 +2100,7 @@ Node* PhaseIdealLoop::clone_iff(PhiNode* phi) { } Node *hit2 = _igvn.hash_find_insert(phi2); if (hit2) { // Hit, toss just made Phi - _igvn.remove_dead_node(phi2); // Remove new phi + _igvn.remove_dead_node(phi2, PhaseIterGVN::NodeOrigin::Speculative); // Remove new phi assert(hit2->is_Phi(), "" ); phi2 = (PhiNode*)hit2; // Use existing phi } else { // Miss @@ -2165,7 +2175,7 @@ CmpNode*PhaseIdealLoop::clone_bool(PhiNode* phi) { // Register with optimizer Node *hit1 = _igvn.hash_find_insert(phi1); if( hit1 ) { // Hit, toss just made Phi - _igvn.remove_dead_node(phi1); // Remove new phi + _igvn.remove_dead_node(phi1, PhaseIterGVN::NodeOrigin::Speculative); // Remove new phi assert( hit1->is_Phi(), "" ); phi1 = (PhiNode*)hit1; // Use existing phi } else { // Miss @@ -2173,7 +2183,7 @@ CmpNode*PhaseIdealLoop::clone_bool(PhiNode* phi) { } Node *hit2 = _igvn.hash_find_insert(phi2); if( hit2 ) { // Hit, toss just made Phi - _igvn.remove_dead_node(phi2); // Remove new phi + _igvn.remove_dead_node(phi2, PhaseIterGVN::NodeOrigin::Speculative); // Remove new phi assert( hit2->is_Phi(), "" ); phi2 = (PhiNode*)hit2; // Use existing phi } else { // Miss @@ -2324,7 +2334,7 @@ void PhaseIdealLoop::clone_loop_handle_data_uses(Node* old, Node_List &old_new, _igvn.register_new_node_with_optimizer(phi); // Register new phi } else { // or // Remove the new phi from the graph and use the hit - _igvn.remove_dead_node(phi); + _igvn.remove_dead_node(phi, phi == prev ? PhaseIterGVN::NodeOrigin::Graph : PhaseIterGVN::NodeOrigin::Speculative); phi = hit; // Use existing phi } set_ctrl(phi, prev); @@ -3472,7 +3482,7 @@ void PhaseIdealLoop::insert_phi_for_loop( Node* use, uint idx, Node* lp_entry_va set_ctrl(phi, lp); } else { // Remove the new phi from the graph and use the hit - _igvn.remove_dead_node(phi); + _igvn.remove_dead_node(phi, PhaseIterGVN::NodeOrigin::Speculative); phi = hit; } _igvn.replace_input_of(use, idx, phi); diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp index 48277eb46d2..8c5dbe7fb48 100644 --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -44,6 +44,7 @@ #include "opto/node.hpp" #include "opto/opaquenode.hpp" #include "opto/phaseX.hpp" +#include "opto/reachability.hpp" #include "opto/rootnode.hpp" #include "opto/runtime.hpp" #include "opto/subnode.hpp" @@ -52,6 +53,7 @@ #include "prims/jvmtiExport.hpp" #include "runtime/continuation.hpp" #include "runtime/sharedRuntime.hpp" +#include "utilities/globalDefinitions.hpp" #include "utilities/macros.hpp" #include "utilities/powerOfTwo.hpp" #if INCLUDE_G1GC @@ -261,74 +263,128 @@ static Node *scan_mem_chain(Node *mem, int alias_idx, int offset, Node *start_me } } -// Generate loads from source of the arraycopy for fields of -// destination needed at a deoptimization point -Node* PhaseMacroExpand::make_arraycopy_load(ArrayCopyNode* ac, intptr_t offset, Node* ctl, Node* mem, BasicType ft, const Type *ftype, AllocateNode *alloc) { +// Determine if there is an interfering store between a rematerialization load and an arraycopy that is in the process +// of being elided. Starting from the given rematerialization load this method starts a BFS traversal upwards through +// the memory graph towards the provided ArrayCopyNode. For every node encountered on the traversal, check that it is +// independent from the provided rematerialization. Returns false if every node on the traversal is independent and +// true otherwise. +bool has_interfering_store(const ArrayCopyNode* ac, LoadNode* load, PhaseGVN* phase) { + assert(ac != nullptr && load != nullptr, "sanity"); + AccessAnalyzer acc(phase, load); + ResourceMark rm; + Unique_Node_List to_visit; + to_visit.push(load->in(MemNode::Memory)); + + for (uint worklist_idx = 0; worklist_idx < to_visit.size(); worklist_idx++) { + Node* mem = to_visit.at(worklist_idx); + + if (mem->is_Proj() && mem->in(0) == ac) { + // Reached the target, so visit what is left on the worklist. + continue; + } + + if (mem->is_Phi()) { + assert(mem->bottom_type() == Type::MEMORY, "do not leave memory graph"); + // Add all non-control inputs of phis to be visited. + for (uint phi_in = 1; phi_in < mem->len(); phi_in++) { + Node* input = mem->in(phi_in); + if (input != nullptr) { + to_visit.push(input); + } + } + continue; + } + + AccessAnalyzer::AccessIndependence ind = acc.detect_access_independence(mem); + if (ind.independent) { + to_visit.push(ind.mem); + } else { + return true; + } + } + // Did not find modification of source element in memory graph. + return false; +} + +// Generate loads from source of the arraycopy for fields of destination needed at a deoptimization point. +// Returns nullptr if the load cannot be created because the arraycopy is not suitable for elimination +// (e.g. copy inside the array with non-constant offsets) or the inputs do not match our assumptions (e.g. +// the arraycopy does not actually write something at the provided offset). +Node* PhaseMacroExpand::make_arraycopy_load(ArrayCopyNode* ac, intptr_t offset, Node* ctl, Node* mem, BasicType ft, const Type* ftype, AllocateNode* alloc) { + assert((ctl == ac->control() && mem == ac->memory()) != (mem != ac->memory() && ctl->is_Proj() && ctl->as_Proj()->is_uncommon_trap_proj()), + "Either the control and memory are the same as for the arraycopy or they are pinned in an uncommon trap."); BasicType bt = ft; const Type *type = ftype; if (ft == T_NARROWOOP) { bt = T_OBJECT; type = ftype->make_oopptr(); } - Node* res = nullptr; + Node* base = ac->in(ArrayCopyNode::Src); + Node* adr = nullptr; + const TypePtr* adr_type = nullptr; + if (ac->is_clonebasic()) { assert(ac->in(ArrayCopyNode::Src) != ac->in(ArrayCopyNode::Dest), "clone source equals destination"); - Node* base = ac->in(ArrayCopyNode::Src); - Node* adr = _igvn.transform(AddPNode::make_with_base(base, _igvn.MakeConX(offset))); - const TypePtr* adr_type = _igvn.type(base)->is_ptr()->add_offset(offset); - MergeMemNode* mergemen = _igvn.transform(MergeMemNode::make(mem))->as_MergeMem(); - BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2(); - res = ArrayCopyNode::load(bs, &_igvn, ctl, mergemen, adr, adr_type, type, bt); + adr = _igvn.transform(AddPNode::make_with_base(base, _igvn.MakeConX(offset))); + adr_type = _igvn.type(base)->is_ptr()->add_offset(offset); } else { - if (ac->modifies(offset, offset, &_igvn, true)) { - assert(ac->in(ArrayCopyNode::Dest) == alloc->result_cast(), "arraycopy destination should be allocation's result"); - uint shift = exact_log2(type2aelembytes(bt)); - Node* src_pos = ac->in(ArrayCopyNode::SrcPos); - Node* dest_pos = ac->in(ArrayCopyNode::DestPos); - const TypeInt* src_pos_t = _igvn.type(src_pos)->is_int(); - const TypeInt* dest_pos_t = _igvn.type(dest_pos)->is_int(); + if (!ac->modifies(offset, offset, &_igvn, true)) { + // If the arraycopy does not copy to this offset, we cannot generate a rematerialization load for it. + return nullptr; + } + assert(ac->in(ArrayCopyNode::Dest) == alloc->result_cast(), "arraycopy destination should be allocation's result"); + uint shift = exact_log2(type2aelembytes(bt)); + Node* src_pos = ac->in(ArrayCopyNode::SrcPos); + Node* dest_pos = ac->in(ArrayCopyNode::DestPos); + const TypeInt* src_pos_t = _igvn.type(src_pos)->is_int(); + const TypeInt* dest_pos_t = _igvn.type(dest_pos)->is_int(); - Node* adr = nullptr; - const TypePtr* adr_type = nullptr; - if (src_pos_t->is_con() && dest_pos_t->is_con()) { - intptr_t off = ((src_pos_t->get_con() - dest_pos_t->get_con()) << shift) + offset; - Node* base = ac->in(ArrayCopyNode::Src); - adr = _igvn.transform(AddPNode::make_with_base(base, _igvn.MakeConX(off))); - adr_type = _igvn.type(base)->is_ptr()->add_offset(off); - if (ac->in(ArrayCopyNode::Src) == ac->in(ArrayCopyNode::Dest)) { - // Don't emit a new load from src if src == dst but try to get the value from memory instead - return value_from_mem(ac->in(TypeFunc::Memory), ctl, ft, ftype, adr_type->isa_oopptr(), alloc); - } - } else { - Node* diff = _igvn.transform(new SubINode(ac->in(ArrayCopyNode::SrcPos), ac->in(ArrayCopyNode::DestPos))); -#ifdef _LP64 - diff = _igvn.transform(new ConvI2LNode(diff)); -#endif - diff = _igvn.transform(new LShiftXNode(diff, _igvn.intcon(shift))); - - Node* off = _igvn.transform(new AddXNode(_igvn.MakeConX(offset), diff)); - Node* base = ac->in(ArrayCopyNode::Src); - adr = _igvn.transform(AddPNode::make_with_base(base, off)); - adr_type = _igvn.type(base)->is_ptr()->add_offset(Type::OffsetBot); - if (ac->in(ArrayCopyNode::Src) == ac->in(ArrayCopyNode::Dest)) { - // Non constant offset in the array: we can't statically - // determine the value - return nullptr; - } + if (src_pos_t->is_con() && dest_pos_t->is_con()) { + intptr_t off = ((src_pos_t->get_con() - dest_pos_t->get_con()) << shift) + offset; + adr = _igvn.transform(AddPNode::make_with_base(base, _igvn.MakeConX(off))); + adr_type = _igvn.type(base)->is_ptr()->add_offset(off); + if (ac->in(ArrayCopyNode::Src) == ac->in(ArrayCopyNode::Dest)) { + // Don't emit a new load from src if src == dst but try to get the value from memory instead + return value_from_mem(ac, ctl, ft, ftype, adr_type->isa_oopptr(), alloc); + } + } else { + Node* diff = _igvn.transform(new SubINode(ac->in(ArrayCopyNode::SrcPos), ac->in(ArrayCopyNode::DestPos))); +#ifdef _LP64 + diff = _igvn.transform(new ConvI2LNode(diff)); +#endif + diff = _igvn.transform(new LShiftXNode(diff, _igvn.intcon(shift))); + + Node* off = _igvn.transform(new AddXNode(_igvn.MakeConX(offset), diff)); + adr = _igvn.transform(AddPNode::make_with_base(base, off)); + adr_type = _igvn.type(base)->is_ptr()->add_offset(Type::OffsetBot); + if (ac->in(ArrayCopyNode::Src) == ac->in(ArrayCopyNode::Dest)) { + // Non constant offset in the array: we can't statically + // determine the value + return nullptr; } - MergeMemNode* mergemen = _igvn.transform(MergeMemNode::make(mem))->as_MergeMem(); - BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2(); - res = ArrayCopyNode::load(bs, &_igvn, ctl, mergemen, adr, adr_type, type, bt); } } - if (res != nullptr) { - if (ftype->isa_narrowoop()) { - // PhaseMacroExpand::scalar_replacement adds DecodeN nodes - res = _igvn.transform(new EncodePNode(res, ftype)); - } - return res; + assert(adr != nullptr && adr_type != nullptr, "sanity"); + + // Create the rematerialization load ... + MergeMemNode* mergemem = _igvn.transform(MergeMemNode::make(mem))->as_MergeMem(); + BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2(); + Node* res = ArrayCopyNode::load(bs, &_igvn, ctl, mergemem, adr, adr_type, type, bt); + assert(res != nullptr, "load should have been created"); + + // ... and ensure that pinning the rematerialization load inside the uncommon path is safe. + if (mem != ac->memory() && ctl->is_Proj() && ctl->as_Proj()->is_uncommon_trap_proj() && res->is_Load() && + has_interfering_store(ac, res->as_Load(), &_igvn)) { + // Not safe: use control and memory from the arraycopy to ensure correct memory state. + _igvn.remove_dead_node(res, PhaseIterGVN::NodeOrigin::Graph); // Clean up the unusable rematerialization load. + return make_arraycopy_load(ac, offset, ac->control(), ac->memory(), ft, ftype, alloc); } - return nullptr; + + if (ftype->isa_narrowoop()) { + // PhaseMacroExpand::scalar_replacement adds DecodeN nodes + res = _igvn.transform(new EncodePNode(res, ftype)); + } + return res; } // @@ -441,21 +497,22 @@ Node *PhaseMacroExpand::value_from_mem_phi(Node *mem, BasicType ft, const Type * } // Search the last value stored into the object's field. -Node *PhaseMacroExpand::value_from_mem(Node *sfpt_mem, Node *sfpt_ctl, BasicType ft, const Type *ftype, const TypeOopPtr *adr_t, AllocateNode *alloc) { +Node* PhaseMacroExpand::value_from_mem(Node* origin, Node* ctl, BasicType ft, const Type* ftype, const TypeOopPtr* adr_t, AllocateNode* alloc) { assert(adr_t->is_known_instance_field(), "instance required"); int instance_id = adr_t->instance_id(); assert((uint)instance_id == alloc->_idx, "wrong allocation"); int alias_idx = C->get_alias_index(adr_t); int offset = adr_t->offset(); + Node* orig_mem = origin->in(TypeFunc::Memory); Node *start_mem = C->start()->proj_out_or_null(TypeFunc::Memory); Node *alloc_ctrl = alloc->in(TypeFunc::Control); Node *alloc_mem = alloc->proj_out_or_null(TypeFunc::Memory, /*io_use:*/false); assert(alloc_mem != nullptr, "Allocation without a memory projection."); VectorSet visited; - bool done = sfpt_mem == alloc_mem; - Node *mem = sfpt_mem; + bool done = orig_mem == alloc_mem; + Node *mem = orig_mem; while (!done) { if (visited.test_set(mem->_idx)) { return nullptr; // found a loop, give up @@ -535,17 +592,22 @@ Node *PhaseMacroExpand::value_from_mem(Node *sfpt_mem, Node *sfpt_ctl, BasicType } } } else if (mem->is_ArrayCopy()) { - Node* ctl = mem->in(0); - Node* m = mem->in(TypeFunc::Memory); - if (sfpt_ctl->is_Proj() && sfpt_ctl->as_Proj()->is_uncommon_trap_proj()) { + // Rematerialize the scalar-replaced array. If possible, pin the loads to the uncommon path of the uncommon trap. + // Check for each element of the source array, whether it was modified. If not, pin both memory and control to + // the uncommon path. Otherwise, use the control and memory state of the arraycopy. Control and memory state must + // come from the same source to prevent anti-dependence problems in the backend. + ArrayCopyNode* ac = mem->as_ArrayCopy(); + Node* ac_ctl = ac->control(); + Node* ac_mem = ac->memory(); + if (ctl->is_Proj() && ctl->as_Proj()->is_uncommon_trap_proj()) { // pin the loads in the uncommon trap path - ctl = sfpt_ctl; - m = sfpt_mem; + ac_ctl = ctl; + ac_mem = orig_mem; } - return make_arraycopy_load(mem->as_ArrayCopy(), offset, ctl, m, ft, ftype, alloc); + return make_arraycopy_load(ac, offset, ac_ctl, ac_mem, ft, ftype, alloc); } } - // Something go wrong. + // Something went wrong. return nullptr; } @@ -622,6 +684,8 @@ bool PhaseMacroExpand::can_eliminate_allocation(PhaseIterGVN* igvn, AllocateNode use->as_ArrayCopy()->is_copyofrange_validated()) && use->in(ArrayCopyNode::Dest) == res) { // ok to eliminate + } else if (use->is_ReachabilityFence() && OptimizeReachabilityFences) { + // ok to eliminate } else if (use->is_SafePoint()) { SafePointNode* sfpt = use->as_SafePoint(); if (sfpt->is_Call() && sfpt->as_Call()->has_non_debug_use(res)) { @@ -717,7 +781,13 @@ void PhaseMacroExpand::undo_previous_scalarizations(GrowableArray 0) { SafePointNode* sfpt_done = safepoints_done.pop(); + + SafePointNode::NodeEdgeTempStorage non_debug_edges_worklist(igvn()); + + sfpt_done->remove_non_debug_edges(non_debug_edges_worklist); + // remove any extra entries we added to the safepoint + assert(sfpt_done->jvms()->endoff() == sfpt_done->req(), "no extra edges past debug info allowed"); uint last = sfpt_done->req() - 1; for (int k = 0; k < nfields; k++) { sfpt_done->del_req(last--); @@ -738,6 +808,9 @@ void PhaseMacroExpand::undo_previous_scalarizations(GrowableArray restore_non_debug_edges(non_debug_edges_worklist); + _igvn._worklist.push(sfpt_done); } } @@ -778,6 +851,8 @@ void PhaseMacroExpand::undo_previous_scalarizations(GrowableArray jvms()->endoff() == sfpt->req(), "no extra edges past debug info allowed"); + // Fields of scalar objs are referenced only at the end // of regular debuginfo at the last (youngest) JVMS. // Record relative start index. @@ -852,7 +927,7 @@ SafePointScalarObjectNode* PhaseMacroExpand::create_scalarized_object_descriptio const TypeOopPtr *field_addr_type = res_type->add_offset(offset)->isa_oopptr(); - Node *field_val = value_from_mem(sfpt->memory(), sfpt->control(), basic_elem_type, field_type, field_addr_type, alloc); + Node* field_val = value_from_mem(sfpt, sfpt->control(), basic_elem_type, field_type, field_addr_type, alloc); // We weren't able to find a value for this field, // give up on eliminating this allocation. @@ -903,17 +978,25 @@ SafePointScalarObjectNode* PhaseMacroExpand::create_scalarized_object_descriptio } // Do scalar replacement. -bool PhaseMacroExpand::scalar_replacement(AllocateNode *alloc, GrowableArray & safepoints) { - GrowableArray safepoints_done; +bool PhaseMacroExpand::scalar_replacement(AllocateNode* alloc, GrowableArray& safepoints) { + GrowableArray safepoints_done; Node* res = alloc->result_cast(); assert(res == nullptr || res->is_CheckCastPP(), "unexpected AllocateNode result"); // Process the safepoint uses while (safepoints.length() > 0) { SafePointNode* sfpt = safepoints.pop(); + + SafePointNode::NodeEdgeTempStorage non_debug_edges_worklist(igvn()); + + // All sfpt inputs are implicitly included into debug info during the scalarization process below. + // Keep non-debug inputs separately, so they stay non-debug. + sfpt->remove_non_debug_edges(non_debug_edges_worklist); + SafePointScalarObjectNode* sobj = create_scalarized_object_description(alloc, sfpt); if (sobj == nullptr) { + sfpt->restore_non_debug_edges(non_debug_edges_worklist); undo_previous_scalarizations(safepoints_done, alloc); return false; } @@ -922,6 +1005,8 @@ bool PhaseMacroExpand::scalar_replacement(AllocateNode *alloc, GrowableArray jvms(); sfpt->replace_edges_in_range(res, sobj, jvms->debug_start(), jvms->debug_end(), &_igvn); + non_debug_edges_worklist.remove_edge_if_present(res); // drop scalarized input from non-debug info + sfpt->restore_non_debug_edges(non_debug_edges_worklist); _igvn._worklist.push(sfpt); // keep it for rollback @@ -973,7 +1058,7 @@ void PhaseMacroExpand::process_users_of_allocation(CallNode *alloc) { } k -= (oc2 - use->outcnt()); } - _igvn.remove_dead_node(use); + _igvn.remove_dead_node(use, PhaseIterGVN::NodeOrigin::Graph); } else if (use->is_ArrayCopy()) { // Disconnect ArrayCopy node ArrayCopyNode* ac = use->as_ArrayCopy(); @@ -1008,17 +1093,19 @@ void PhaseMacroExpand::process_users_of_allocation(CallNode *alloc) { // src can be top at this point if src and dest of the // arraycopy were the same if (src->outcnt() == 0 && !src->is_top()) { - _igvn.remove_dead_node(src); + _igvn.remove_dead_node(src, PhaseIterGVN::NodeOrigin::Graph); } } _igvn._worklist.push(ac); + } else if (use->is_ReachabilityFence() && OptimizeReachabilityFences) { + use->as_ReachabilityFence()->clear_referent(_igvn); // redundant fence; will be removed during IGVN } else { eliminate_gc_barrier(use); } j -= (oc1 - res->outcnt()); } assert(res->outcnt() == 0, "all uses of allocated objects must be deleted"); - _igvn.remove_dead_node(res); + _igvn.remove_dead_node(res, PhaseIterGVN::NodeOrigin::Graph); } // @@ -1502,7 +1589,7 @@ void PhaseMacroExpand::expand_allocate_common( transform_later(_callprojs.fallthrough_memproj); } migrate_outs(_callprojs.catchall_memproj, _callprojs.fallthrough_memproj); - _igvn.remove_dead_node(_callprojs.catchall_memproj); + _igvn.remove_dead_node(_callprojs.catchall_memproj, PhaseIterGVN::NodeOrigin::Graph); } // An allocate node has separate i_o projections for the uses on the control @@ -1521,7 +1608,7 @@ void PhaseMacroExpand::expand_allocate_common( transform_later(_callprojs.fallthrough_ioproj); } migrate_outs(_callprojs.catchall_ioproj, _callprojs.fallthrough_ioproj); - _igvn.remove_dead_node(_callprojs.catchall_ioproj); + _igvn.remove_dead_node(_callprojs.catchall_ioproj, PhaseIterGVN::NodeOrigin::Graph); } // if we generated only a slow call, we are done @@ -1585,11 +1672,11 @@ void PhaseMacroExpand::yank_alloc_node(AllocateNode* alloc) { --i; // back up iterator } assert(_callprojs.resproj->outcnt() == 0, "all uses must be deleted"); - _igvn.remove_dead_node(_callprojs.resproj); + _igvn.remove_dead_node(_callprojs.resproj, PhaseIterGVN::NodeOrigin::Graph); } if (_callprojs.fallthrough_catchproj != nullptr) { migrate_outs(_callprojs.fallthrough_catchproj, ctrl); - _igvn.remove_dead_node(_callprojs.fallthrough_catchproj); + _igvn.remove_dead_node(_callprojs.fallthrough_catchproj, PhaseIterGVN::NodeOrigin::Graph); } if (_callprojs.catchall_catchproj != nullptr) { _igvn.rehash_node_delayed(_callprojs.catchall_catchproj); @@ -1597,16 +1684,16 @@ void PhaseMacroExpand::yank_alloc_node(AllocateNode* alloc) { } if (_callprojs.fallthrough_proj != nullptr) { Node* catchnode = _callprojs.fallthrough_proj->unique_ctrl_out(); - _igvn.remove_dead_node(catchnode); - _igvn.remove_dead_node(_callprojs.fallthrough_proj); + _igvn.remove_dead_node(catchnode, PhaseIterGVN::NodeOrigin::Graph); + _igvn.remove_dead_node(_callprojs.fallthrough_proj, PhaseIterGVN::NodeOrigin::Graph); } if (_callprojs.fallthrough_memproj != nullptr) { migrate_outs(_callprojs.fallthrough_memproj, mem); - _igvn.remove_dead_node(_callprojs.fallthrough_memproj); + _igvn.remove_dead_node(_callprojs.fallthrough_memproj, PhaseIterGVN::NodeOrigin::Graph); } if (_callprojs.fallthrough_ioproj != nullptr) { migrate_outs(_callprojs.fallthrough_ioproj, i_o); - _igvn.remove_dead_node(_callprojs.fallthrough_ioproj); + _igvn.remove_dead_node(_callprojs.fallthrough_ioproj, PhaseIterGVN::NodeOrigin::Graph); } if (_callprojs.catchall_memproj != nullptr) { _igvn.rehash_node_delayed(_callprojs.catchall_memproj); @@ -1625,7 +1712,7 @@ void PhaseMacroExpand::yank_alloc_node(AllocateNode* alloc) { } } #endif - _igvn.remove_dead_node(alloc); + _igvn.remove_dead_node(alloc, PhaseIterGVN::NodeOrigin::Graph); } void PhaseMacroExpand::expand_initialize_membar(AllocateNode* alloc, InitializeNode* init, @@ -2500,6 +2587,7 @@ void PhaseMacroExpand::eliminate_macro_nodes() { assert(n->Opcode() == Op_LoopLimit || n->Opcode() == Op_ModD || n->Opcode() == Op_ModF || + n->Opcode() == Op_PowD || n->is_OpaqueConstantBool() || n->is_OpaqueInitializedAssertionPredicate() || n->Opcode() == Op_MaxL || @@ -2656,18 +2744,11 @@ bool PhaseMacroExpand::expand_macro_nodes() { default: switch (n->Opcode()) { case Op_ModD: - case Op_ModF: { - CallNode* mod_macro = n->as_Call(); - CallNode* call = new CallLeafPureNode(mod_macro->tf(), mod_macro->entry_point(), mod_macro->_name); - call->init_req(TypeFunc::Control, mod_macro->in(TypeFunc::Control)); - call->init_req(TypeFunc::I_O, C->top()); - call->init_req(TypeFunc::Memory, C->top()); - call->init_req(TypeFunc::ReturnAdr, C->top()); - call->init_req(TypeFunc::FramePtr, C->top()); - for (unsigned int i = 0; i < mod_macro->tf()->domain()->cnt() - TypeFunc::Parms; i++) { - call->init_req(TypeFunc::Parms + i, mod_macro->in(TypeFunc::Parms + i)); - } - _igvn.replace_node(mod_macro, call); + case Op_ModF: + case Op_PowD: { + CallLeafPureNode* call_macro = n->as_CallLeafPure(); + CallLeafPureNode* call = call_macro->inline_call_leaf_pure_node(); + _igvn.replace_node(call_macro, call); transform_later(call); break; } diff --git a/src/hotspot/share/opto/macro.hpp b/src/hotspot/share/opto/macro.hpp index 59a455cae6d..e4b20402e63 100644 --- a/src/hotspot/share/opto/macro.hpp +++ b/src/hotspot/share/opto/macro.hpp @@ -104,7 +104,7 @@ private: address slow_call_address, Node* valid_length_test); void yank_alloc_node(AllocateNode* alloc); - Node *value_from_mem(Node *mem, Node *ctl, BasicType ft, const Type *ftype, const TypeOopPtr *adr_t, AllocateNode *alloc); + Node* value_from_mem(Node* start, Node* ctl, BasicType ft, const Type* ftype, const TypeOopPtr* adr_t, AllocateNode* alloc); Node *value_from_mem_phi(Node *mem, BasicType ft, const Type *ftype, const TypeOopPtr *adr_t, AllocateNode *alloc, Node_Stack *value_phis, int level); bool eliminate_boxing_node(CallStaticJavaNode *boxing); @@ -205,7 +205,7 @@ private: Node* klass_node, Node* length, Node* size_in_bytes); - Node* make_arraycopy_load(ArrayCopyNode* ac, intptr_t offset, Node* ctl, Node* mem, BasicType ft, const Type *ftype, AllocateNode *alloc); + Node* make_arraycopy_load(ArrayCopyNode* ac, intptr_t offset, Node* ctl, Node* mem, BasicType ft, const Type* ftype, AllocateNode* alloc); public: PhaseMacroExpand(PhaseIterGVN &igvn) : Phase(Macro_Expand), _igvn(igvn) { diff --git a/src/hotspot/share/opto/memnode.cpp b/src/hotspot/share/opto/memnode.cpp index 3cd553e4bd1..a49d4708d32 100644 --- a/src/hotspot/share/opto/memnode.cpp +++ b/src/hotspot/share/opto/memnode.cpp @@ -1156,40 +1156,38 @@ Node* LoadNode::can_see_arraycopy_value(Node* st, PhaseGVN* phase) const { return nullptr; } - -//---------------------------can_see_stored_value------------------------------ // This routine exists to make sure this set of tests is done the same // everywhere. We need to make a coordinated change: first LoadNode::Ideal // will change the graph shape in a way which makes memory alive twice at the // same time (uses the Oracle model of aliasing), then some // LoadXNode::Identity will fold things back to the equivalence-class model // of aliasing. -Node* MemNode::can_see_stored_value(Node* st, PhaseValues* phase) const { +Node* LoadNode::can_see_stored_value_through_membars(Node* st, PhaseValues* phase) const { Node* ld_adr = in(MemNode::Address); - intptr_t ld_off = 0; - Node* ld_base = AddPNode::Ideal_base_and_offset(ld_adr, phase, ld_off); - Node* ld_alloc = AllocateNode::Ideal_allocation(ld_base); const TypeInstPtr* tp = phase->type(ld_adr)->isa_instptr(); Compile::AliasType* atp = (tp != nullptr) ? phase->C->alias_type(tp) : nullptr; - // This is more general than load from boxing objects. + if (skip_through_membars(atp, tp, phase->C->eliminate_boxing())) { uint alias_idx = atp->index(); Node* result = nullptr; Node* current = st; - // Skip through chains of MemBarNodes checking the MergeMems for - // new states for the slice of this load. Stop once any other - // kind of node is encountered. Loads from final memory can skip - // through any kind of MemBar but normal loads shouldn't skip - // through MemBarAcquire since the could allow them to move out of - // a synchronized region. It is not safe to step over MemBarCPUOrder, - // because alias info above them may be inaccurate (e.g., due to - // mixed/mismatched unsafe accesses). + // Skip through chains of MemBarNodes checking the MergeMems for new states for the slice of + // this load. Stop once any other kind of node is encountered. + // + // In principle, folding a load is moving it up until it meets a matching store. + // + // store(ptr, v); store(ptr, v); store(ptr, v); + // membar1; -> membar1; -> load(ptr); + // membar2; load(ptr); membar1; + // load(ptr); membar2; membar2; + // + // So, we can decide which kinds of barriers we can walk past. It is not safe to step over + // MemBarCPUOrder, even if the memory is not rewritable, because alias info above them may be + // inaccurate (e.g., due to mixed/mismatched unsafe accesses). bool is_final_mem = !atp->is_rewritable(); while (current->is_Proj()) { int opc = current->in(0)->Opcode(); - if ((is_final_mem && (opc == Op_MemBarAcquire || - opc == Op_MemBarAcquireLock || - opc == Op_LoadFence)) || + if ((is_final_mem && (opc == Op_MemBarAcquire || opc == Op_MemBarAcquireLock || opc == Op_LoadFence)) || opc == Op_MemBarRelease || opc == Op_StoreFence || opc == Op_MemBarReleaseLock || @@ -1216,6 +1214,17 @@ Node* MemNode::can_see_stored_value(Node* st, PhaseValues* phase) const { } } + return can_see_stored_value(st, phase); +} + +// If st is a store to the same location as this, return the stored value +Node* MemNode::can_see_stored_value(Node* st, PhaseValues* phase) const { + Node* ld_adr = in(MemNode::Address); + intptr_t ld_off = 0; + Node* ld_base = AddPNode::Ideal_base_and_offset(ld_adr, phase, ld_off); + Node* ld_alloc = AllocateNode::Ideal_allocation(ld_base); + const TypeInstPtr* tp = phase->type(ld_adr)->isa_instptr(); + // Loop around twice in the case Load -> Initialize -> Store. // (See PhaseIterGVN::add_users_to_worklist, which knows about this case.) for (int trip = 0; trip <= 1; trip++) { @@ -1344,7 +1353,7 @@ Node* LoadNode::Identity(PhaseGVN* phase) { // If the previous store-maker is the right kind of Store, and the store is // to the same address, then we are equal to the value stored. Node* mem = in(Memory); - Node* value = can_see_stored_value(mem, phase); + Node* value = can_see_stored_value_through_membars(mem, phase); if( value ) { // byte, short & char stores truncate naturally. // A load has to load the truncated value which requires @@ -1662,11 +1671,15 @@ bool LoadNode::can_split_through_phi_base(PhaseGVN* phase) { intptr_t ignore = 0; Node* base = AddPNode::Ideal_base_and_offset(address, phase, ignore); + if (base == nullptr) { + return false; + } + if (base->is_CastPP()) { base = base->in(1); } - if (req() > 3 || base == nullptr || !base->is_Phi()) { + if (req() > 3 || !base->is_Phi()) { return false; } @@ -1889,7 +1902,7 @@ Node* LoadNode::split_through_phi(PhaseGVN* phase, bool ignore_missing_instance_ } } if (x != the_clone && the_clone != nullptr) { - igvn->remove_dead_node(the_clone); + igvn->remove_dead_node(the_clone, PhaseIterGVN::NodeOrigin::Speculative); } phi->set_req(i, x); } @@ -2042,7 +2055,7 @@ Node *LoadNode::Ideal(PhaseGVN *phase, bool can_reshape) { // (c) See if we can fold up on the spot, but don't fold up here. // Fold-up might require truncation (for LoadB/LoadS/LoadUS) or // just return a prior value, which is done by Identity calls. - if (can_see_stored_value(prev_mem, phase)) { + if (can_see_stored_value_through_membars(prev_mem, phase)) { // Make ready for step (d): set_req_X(MemNode::Memory, prev_mem, phase); return this; @@ -2099,7 +2112,7 @@ const Type* LoadNode::Value(PhaseGVN* phase) const { Compile* C = phase->C; // If load can see a previous constant store, use that. - Node* value = can_see_stored_value(mem, phase); + Node* value = can_see_stored_value_through_membars(mem, phase); if (value != nullptr && value->is_Con()) { assert(value->bottom_type()->higher_equal(_type), "sanity"); return value->bottom_type(); @@ -2350,7 +2363,7 @@ uint LoadNode::match_edge(uint idx) const { // Node* LoadBNode::Ideal(PhaseGVN* phase, bool can_reshape) { Node* mem = in(MemNode::Memory); - Node* value = can_see_stored_value(mem,phase); + Node* value = can_see_stored_value_through_membars(mem, phase); if (value != nullptr) { Node* narrow = Compile::narrow_value(T_BYTE, value, _type, phase, false); if (narrow != value) { @@ -2363,7 +2376,7 @@ Node* LoadBNode::Ideal(PhaseGVN* phase, bool can_reshape) { const Type* LoadBNode::Value(PhaseGVN* phase) const { Node* mem = in(MemNode::Memory); - Node* value = can_see_stored_value(mem,phase); + Node* value = can_see_stored_value_through_membars(mem, phase); if (value != nullptr && value->is_Con() && !value->bottom_type()->higher_equal(_type)) { // If the input to the store does not fit with the load's result type, @@ -2384,7 +2397,7 @@ const Type* LoadBNode::Value(PhaseGVN* phase) const { // Node* LoadUBNode::Ideal(PhaseGVN* phase, bool can_reshape) { Node* mem = in(MemNode::Memory); - Node* value = can_see_stored_value(mem, phase); + Node* value = can_see_stored_value_through_membars(mem, phase); if (value != nullptr) { Node* narrow = Compile::narrow_value(T_BOOLEAN, value, _type, phase, false); if (narrow != value) { @@ -2397,7 +2410,7 @@ Node* LoadUBNode::Ideal(PhaseGVN* phase, bool can_reshape) { const Type* LoadUBNode::Value(PhaseGVN* phase) const { Node* mem = in(MemNode::Memory); - Node* value = can_see_stored_value(mem,phase); + Node* value = can_see_stored_value_through_membars(mem, phase); if (value != nullptr && value->is_Con() && !value->bottom_type()->higher_equal(_type)) { // If the input to the store does not fit with the load's result type, @@ -2418,7 +2431,7 @@ const Type* LoadUBNode::Value(PhaseGVN* phase) const { // Node* LoadUSNode::Ideal(PhaseGVN* phase, bool can_reshape) { Node* mem = in(MemNode::Memory); - Node* value = can_see_stored_value(mem,phase); + Node* value = can_see_stored_value_through_membars(mem, phase); if (value != nullptr) { Node* narrow = Compile::narrow_value(T_CHAR, value, _type, phase, false); if (narrow != value) { @@ -2431,7 +2444,7 @@ Node* LoadUSNode::Ideal(PhaseGVN* phase, bool can_reshape) { const Type* LoadUSNode::Value(PhaseGVN* phase) const { Node* mem = in(MemNode::Memory); - Node* value = can_see_stored_value(mem,phase); + Node* value = can_see_stored_value_through_membars(mem, phase); if (value != nullptr && value->is_Con() && !value->bottom_type()->higher_equal(_type)) { // If the input to the store does not fit with the load's result type, @@ -2452,7 +2465,7 @@ const Type* LoadUSNode::Value(PhaseGVN* phase) const { // Node* LoadSNode::Ideal(PhaseGVN* phase, bool can_reshape) { Node* mem = in(MemNode::Memory); - Node* value = can_see_stored_value(mem,phase); + Node* value = can_see_stored_value_through_membars(mem, phase); if (value != nullptr) { Node* narrow = Compile::narrow_value(T_SHORT, value, _type, phase, false); if (narrow != value) { @@ -2465,7 +2478,7 @@ Node* LoadSNode::Ideal(PhaseGVN* phase, bool can_reshape) { const Type* LoadSNode::Value(PhaseGVN* phase) const { Node* mem = in(MemNode::Memory); - Node* value = can_see_stored_value(mem,phase); + Node* value = can_see_stored_value_through_membars(mem, phase); if (value != nullptr && value->is_Con() && !value->bottom_type()->higher_equal(_type)) { // If the input to the store does not fit with the load's result type, @@ -2486,7 +2499,6 @@ Node* LoadKlassNode::make(PhaseGVN& gvn, Node* mem, Node* adr, const TypePtr* at assert(adr_type != nullptr, "expecting TypeKlassPtr"); #ifdef _LP64 if (adr_type->is_ptr_to_narrowklass()) { - assert(UseCompressedClassPointers, "no compressed klasses"); Node* load_klass = gvn.transform(new LoadNKlassNode(mem, adr, at, tk->make_narrowklass(), MemNode::unordered)); return new DecodeNKlassNode(load_klass, load_klass->bottom_type()->make_ptr()); } @@ -2816,8 +2828,7 @@ StoreNode* StoreNode::make(PhaseGVN& gvn, Node* ctl, Node* mem, Node* adr, const val = gvn.transform(new EncodePNode(val, val->bottom_type()->make_narrowoop())); return new StoreNNode(ctl, mem, adr, adr_type, val, mo); } else if (adr->bottom_type()->is_ptr_to_narrowklass() || - (UseCompressedClassPointers && val->bottom_type()->isa_klassptr() && - adr->bottom_type()->isa_rawptr())) { + (val->bottom_type()->isa_klassptr() && adr->bottom_type()->isa_rawptr())) { val = gvn.transform(new EncodePKlassNode(val, val->bottom_type()->make_narrowklass())); return new StoreNKlassNode(ctl, mem, adr, adr_type, val, mo); } @@ -4074,7 +4085,7 @@ uint LoadStoreNode::ideal_reg() const { bool LoadStoreNode::result_not_used() const { for (DUIterator_Fast imax, i = fast_outs(imax); i < imax; i++) { Node *x = fast_out(i); - if (x->Opcode() == Op_SCMemProj) { + if (x->Opcode() == Op_SCMemProj || x->is_ReachabilityFence()) { continue; } if (x->bottom_type() == TypeTuple::MEMBAR && diff --git a/src/hotspot/share/opto/memnode.hpp b/src/hotspot/share/opto/memnode.hpp index 7fa238f574d..8efb5521e7c 100644 --- a/src/hotspot/share/opto/memnode.hpp +++ b/src/hotspot/share/opto/memnode.hpp @@ -266,6 +266,7 @@ protected: const Type* const _type; // What kind of value is loaded? virtual Node* find_previous_arraycopy(PhaseValues* phase, Node* ld_alloc, Node*& mem, bool can_see_stored_value) const; + Node* can_see_stored_value_through_membars(Node* st, PhaseValues* phase) const; public: LoadNode(Node *c, Node *mem, Node *adr, const TypePtr* at, const Type *rt, MemOrd mo, ControlDependency control_dependency) diff --git a/src/hotspot/share/opto/narrowptrnode.cpp b/src/hotspot/share/opto/narrowptrnode.cpp index 7f86b8caecf..8b91bfaa944 100644 --- a/src/hotspot/share/opto/narrowptrnode.cpp +++ b/src/hotspot/share/opto/narrowptrnode.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -102,7 +102,7 @@ const Type* EncodePKlassNode::Value(PhaseGVN* phase) const { if (t == Type::TOP) return Type::TOP; assert (t != TypePtr::NULL_PTR, "null klass?"); - assert(UseCompressedClassPointers && t->isa_klassptr(), "only klass ptr here"); + assert(t->isa_klassptr(), "only klass ptr here"); return t->make_narrowklass(); } diff --git a/src/hotspot/share/opto/node.cpp b/src/hotspot/share/opto/node.cpp index cb5795a1250..997ce92fe1c 100644 --- a/src/hotspot/share/opto/node.cpp +++ b/src/hotspot/share/opto/node.cpp @@ -38,6 +38,7 @@ #include "opto/matcher.hpp" #include "opto/node.hpp" #include "opto/opcodes.hpp" +#include "opto/reachability.hpp" #include "opto/regmask.hpp" #include "opto/rootnode.hpp" #include "opto/type.hpp" @@ -503,6 +504,9 @@ Node *Node::clone() const { if (is_expensive()) { C->add_expensive_node(n); } + if (is_ReachabilityFence()) { + C->add_reachability_fence(n->as_ReachabilityFence()); + } if (for_post_loop_opts_igvn()) { // Don't add cloned node to Compile::_for_post_loop_opts_igvn list automatically. // If it is applicable, it will happen anyway when the cloned node is registered with IGVN. @@ -622,6 +626,9 @@ void Node::destruct(PhaseValues* phase) { if (is_expensive()) { compile->remove_expensive_node(this); } + if (is_ReachabilityFence()) { + compile->remove_reachability_fence(as_ReachabilityFence()); + } if (is_OpaqueTemplateAssertionPredicate()) { compile->remove_template_assertion_predicate_opaque(as_OpaqueTemplateAssertionPredicate()); } @@ -2994,6 +3001,25 @@ bool Node::is_data_proj_of_pure_function(const Node* maybe_pure_function) const return Opcode() == Op_Proj && as_Proj()->_con == TypeFunc::Parms && maybe_pure_function->is_CallLeafPure(); } +//--------------------------has_non_debug_uses------------------------------ +// Checks whether the node has any non-debug uses or not. +bool Node::has_non_debug_uses() const { + for (DUIterator_Fast imax, i = fast_outs(imax); i < imax; i++) { + Node* u = fast_out(i); + if (u->is_SafePoint()) { + if (u->is_Call() && u->as_Call()->has_non_debug_use(this)) { + return true; + } + // Non-call safepoints have only debug uses. + } else if (u->is_ReachabilityFence()) { + // Reachability fence is treated as debug use. + } else { + return true; // everything else is conservatively treated as non-debug use + } + } + return false; // no non-debug uses found +} + //============================================================================= //------------------------------yank------------------------------------------- // Find and remove @@ -3069,7 +3095,7 @@ void Node_Stack::grow() { size_t old_top = pointer_delta(_inode_top,_inodes,sizeof(INode)); // save _top size_t old_max = pointer_delta(_inode_max,_inodes,sizeof(INode)); size_t max = old_max << 1; // max * 2 - _inodes = REALLOC_ARENA_ARRAY(_a, INode, _inodes, old_max, max); + _inodes = REALLOC_ARENA_ARRAY(_a, _inodes, old_max, max); _inode_max = _inodes + max; _inode_top = _inodes + old_top; // restore _top } @@ -3187,4 +3213,3 @@ Node* TypeNode::Ideal(PhaseGVN* phase, bool can_reshape) { return Node::Ideal(phase, can_reshape); } - diff --git a/src/hotspot/share/opto/node.hpp b/src/hotspot/share/opto/node.hpp index 46b89aa2c5f..8c6622e643e 100644 --- a/src/hotspot/share/opto/node.hpp +++ b/src/hotspot/share/opto/node.hpp @@ -168,6 +168,7 @@ class Pipeline; class PopulateIndexNode; class ProjNode; class RangeCheckNode; +class ReachabilityFenceNode; class ReductionNode; class RegMask; class RegionNode; @@ -452,6 +453,9 @@ public: // Check whether node has become unreachable bool is_unreachable(PhaseIterGVN &igvn) const; + // Does the node have any immediate non-debug uses? + bool has_non_debug_uses() const; + // Set a required input edge, also updates corresponding output edge void add_req( Node *n ); // Append a NEW required input void add_req( Node *n0, Node *n1 ) { @@ -824,6 +828,7 @@ public: DEFINE_CLASS_ID(Move, Node, 20) DEFINE_CLASS_ID(LShift, Node, 21) DEFINE_CLASS_ID(Neg, Node, 22) + DEFINE_CLASS_ID(ReachabilityFence, Node, 23) _max_classes = ClassMask_Neg }; @@ -1013,6 +1018,7 @@ public: DEFINE_CLASS_QUERY(PCTable) DEFINE_CLASS_QUERY(Phi) DEFINE_CLASS_QUERY(Proj) + DEFINE_CLASS_QUERY(ReachabilityFence) DEFINE_CLASS_QUERY(Reduction) DEFINE_CLASS_QUERY(Region) DEFINE_CLASS_QUERY(Root) @@ -1180,6 +1186,7 @@ public: return nullptr; } assert(!res->depends_only_on_test(), "the result must not depends_only_on_test"); + assert(Opcode() == res->Opcode(), "pinning must result in the same kind of node %s - %s", Name(), res->Name()); return res; } diff --git a/src/hotspot/share/opto/parse.hpp b/src/hotspot/share/opto/parse.hpp index 397a7796f88..5118019fc31 100644 --- a/src/hotspot/share/opto/parse.hpp +++ b/src/hotspot/share/opto/parse.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -356,6 +356,7 @@ class Parse : public GraphKit { bool _wrote_stable; // Did we write a @Stable field? bool _wrote_fields; // Did we write any field? Node* _alloc_with_final_or_stable; // An allocation node with final or @Stable field + Node* _stress_rf_hook; // StressReachabilityFences support // Variables which track Java semantics during bytecode parsing: @@ -474,8 +475,8 @@ class Parse : public GraphKit { void merge( int target_bci); // Same as plain merge, except that it allocates a new path number. void merge_new_path( int target_bci); - // Merge the current mapping into an exception handler. - void merge_exception(int target_bci); + // Push the exception oop and merge the current mapping into an exception handler. + void push_and_merge_exception(int target_bci, Node* ex_oop); // Helper: Merge the current mapping into the given basic block void merge_common(Block* target, int pnum); // Helper functions for merging individual cells. diff --git a/src/hotspot/share/opto/parse1.cpp b/src/hotspot/share/opto/parse1.cpp index 647e8515b30..6a400631bff 100644 --- a/src/hotspot/share/opto/parse1.cpp +++ b/src/hotspot/share/opto/parse1.cpp @@ -369,6 +369,15 @@ void Parse::load_interpreter_state(Node* osr_buf) { continue; } set_local(index, check_interpreter_type(l, type, bad_type_exit)); + if (StressReachabilityFences && type->isa_oopptr() != nullptr) { + // Keep all oop locals alive until the method returns as if there are + // reachability fences for them at the end of the method. + Node* loc = local(index); + if (loc->bottom_type() != TypePtr::NULL_PTR) { + assert(loc->bottom_type()->isa_oopptr() != nullptr, "%s", Type::str(loc->bottom_type())); + _stress_rf_hook->add_req(loc); + } + } } for (index = 0; index < sp(); index++) { @@ -377,6 +386,15 @@ void Parse::load_interpreter_state(Node* osr_buf) { if (l->is_top()) continue; // nothing here const Type *type = osr_block->stack_type_at(index); set_stack(index, check_interpreter_type(l, type, bad_type_exit)); + if (StressReachabilityFences && type->isa_oopptr() != nullptr) { + // Keep all oops on stack alive until the method returns as if there are + // reachability fences for them at the end of the method. + Node* stk = stack(index); + if (stk->bottom_type() != TypePtr::NULL_PTR) { + assert(stk->bottom_type()->isa_oopptr() != nullptr, "%s", Type::str(stk->bottom_type())); + _stress_rf_hook->add_req(stk); + } + } } if (bad_type_exit->control()->req() > 1) { @@ -411,6 +429,7 @@ Parse::Parse(JVMState* caller, ciMethod* parse_method, float expected_uses) _wrote_stable = false; _wrote_fields = false; _alloc_with_final_or_stable = nullptr; + _stress_rf_hook = (StressReachabilityFences ? new Node(1) : nullptr); _block = nullptr; _first_return = true; _replaced_nodes_for_exceptions = false; @@ -642,6 +661,11 @@ Parse::Parse(JVMState* caller, ciMethod* parse_method, float expected_uses) if (log) log->done("parse nodes='%d' live='%d' memory='%zu'", C->unique(), C->live_nodes(), C->node_arena()->used()); + + if (StressReachabilityFences) { + _stress_rf_hook->destruct(&_gvn); + _stress_rf_hook = nullptr; + } } //---------------------------do_all_blocks------------------------------------- @@ -1194,6 +1218,14 @@ SafePointNode* Parse::create_entry_map() { return entry_map; } +//-----------------------is_auto_boxed_primitive------------------------------ +// Helper method to detect auto-boxed primitives (result of valueOf() call). +static bool is_auto_boxed_primitive(Node* n) { + return (n->is_Proj() && n->as_Proj()->_con == TypeFunc::Parms && + n->in(0)->is_CallJava() && + n->in(0)->as_CallJava()->method()->is_boxing_method()); +} + //-----------------------------do_method_entry-------------------------------- // Emit any code needed in the pseudo-block before BCI zero. // The main thing to do is lock the receiver of a synchronized method. @@ -1207,6 +1239,19 @@ void Parse::do_method_entry() { make_dtrace_method_entry(method()); } + if (StressReachabilityFences) { + // Keep all oop arguments alive until the method returns as if there are + // reachability fences for them at the end of the method. + int max_locals = jvms()->loc_size(); + for (int idx = 0; idx < max_locals; idx++) { + Node* loc = local(idx); + if (loc->bottom_type()->isa_oopptr() != nullptr && + !is_auto_boxed_primitive(loc)) { // ignore auto-boxed primitives + _stress_rf_hook->add_req(loc); + } + } + } + #ifdef ASSERT // Narrow receiver type when it is too broad for the method being parsed. if (!method()->is_static()) { @@ -1651,9 +1696,14 @@ void Parse::merge_new_path(int target_bci) { } //-------------------------merge_exception------------------------------------- -// Merge the current mapping into the basic block starting at bci -// The ex_oop must be pushed on the stack, unlike throw_to_exit. -void Parse::merge_exception(int target_bci) { +// Push the given ex_oop onto the stack, then merge the current mapping into +// the basic block starting at target_bci. +void Parse::push_and_merge_exception(int target_bci, Node* ex_oop) { + // Add the safepoint before trimming the stack and pushing the exception oop. + // We could add the safepoint after, but then the bci would also need to be + // advanced to target_bci first, so the stack state matches. + maybe_add_safepoint(target_bci); + push_ex_oop(ex_oop); // Push exception oop for handler #ifdef ASSERT if (target_bci <= bci()) { C->set_exception_backedge(); @@ -2192,6 +2242,15 @@ void Parse::return_current(Node* value) { call_register_finalizer(); } + if (StressReachabilityFences) { + // Insert reachability fences for all oop arguments at the end of the method. + for (uint i = 1; i < _stress_rf_hook->req(); i++) { + Node* referent = _stress_rf_hook->in(i); + assert(referent->bottom_type()->isa_oopptr(), "%s", Type::str(referent->bottom_type())); + insert_reachability_fence(referent); + } + } + // Do not set_parse_bci, so that return goo is credited to the return insn. set_bci(InvocationEntryBci); if (method()->is_synchronized()) { diff --git a/src/hotspot/share/opto/parse2.cpp b/src/hotspot/share/opto/parse2.cpp index 7f41870ccea..d732e6f04e1 100644 --- a/src/hotspot/share/opto/parse2.cpp +++ b/src/hotspot/share/opto/parse2.cpp @@ -1757,6 +1757,12 @@ static bool match_type_check(PhaseGVN& gvn, // Bool(CmpP(LoadKlass(obj._klass), ConP(Foo.klass)), [eq]) // or the narrowOop equivalent. (*obj) = extract_obj_from_klass_load(&gvn, val); + // Some klass comparisons are not directly in the form + // Bool(CmpP(LoadKlass(obj._klass), ConP(Foo.klass)), [eq]), + // e.g. Bool(CmpP(CastPP(LoadKlass(...)), ConP(klass)), [eq]). + // These patterns with nullable klasses arise from example from + // load_array_klass_from_mirror. + if (*obj == nullptr) { return false; } (*cast_type) = tcon->isa_klassptr()->as_instance_type(); return true; // found } @@ -1797,8 +1803,8 @@ static bool match_type_check(PhaseGVN& gvn, assert(idx == 1 || idx == 2, ""); Node* vcon = val->in(idx); - assert(val->find_edge(con) > 0, ""); if ((btest == BoolTest::eq && vcon == con) || (btest == BoolTest::ne && vcon != con)) { + assert(val->find_edge(con) > 0, "mismatch"); SubTypeCheckNode* sub = b1->in(1)->as_SubTypeCheck(); Node* obj_or_subklass = sub->in(SubTypeCheckNode::ObjOrSubKlass); Node* superklass = sub->in(SubTypeCheckNode::SuperKlass); @@ -1827,17 +1833,21 @@ void Parse::sharpen_type_after_if(BoolTest::mask btest, &obj, &cast_type)) { assert(obj != nullptr && cast_type != nullptr, "missing type check info"); const Type* obj_type = _gvn.type(obj); - const TypeOopPtr* tboth = obj_type->join_speculative(cast_type)->isa_oopptr(); - if (tboth != nullptr && tboth != obj_type && tboth->higher_equal(obj_type)) { + const Type* tboth = obj_type->filter_speculative(cast_type); + assert(tboth->higher_equal(obj_type) && tboth->higher_equal(cast_type), "sanity"); + if (tboth == Type::TOP && KillPathsReachableByDeadTypeNode) { + // Let dead type node cleaning logic prune effectively dead path for us. + // CheckCastPP::Value() == TOP and it will trigger the cleanup during GVN. + // Don't materialize the cast when cleanup is disabled, because + // it kills data and control leaving IR in broken state. + tboth = cast_type; + } + if (tboth != Type::TOP && tboth != obj_type) { int obj_in_map = map()->find_edge(obj); - JVMState* jvms = this->jvms(); if (obj_in_map >= 0 && - (jvms->is_loc(obj_in_map) || jvms->is_stk(obj_in_map))) { + (jvms()->is_loc(obj_in_map) || jvms()->is_stk(obj_in_map))) { TypeNode* ccast = new CheckCastPPNode(control(), obj, tboth); - const Type* tcc = ccast->as_Type()->type(); - assert(tcc != obj_type && tcc->higher_equal(obj_type), "must improve"); - // Delay transform() call to allow recovery of pre-cast value - // at the control merge. + // Delay transform() call to allow recovery of pre-cast value at the control merge. _gvn.set_type_bottom(ccast); record_for_igvn(ccast); // Here's the payoff. diff --git a/src/hotspot/share/opto/phase.cpp b/src/hotspot/share/opto/phase.cpp index 5603033ce69..3f1866990e2 100644 --- a/src/hotspot/share/opto/phase.cpp +++ b/src/hotspot/share/opto/phase.cpp @@ -90,6 +90,9 @@ void Phase::print_timers() { tty->print_cr (" Prune Useless: %7.3f s", timers[_t_vector_pru].seconds()); tty->print_cr (" Renumber Live: %7.3f s", timers[_t_renumberLive].seconds()); tty->print_cr (" IdealLoop: %7.3f s", timers[_t_idealLoop].seconds()); + tty->print_cr (" ReachabilityFence: %7.3f s", timers[_t_reachability].seconds()); + tty->print_cr (" Optimize: %7.3f s", timers[_t_reachability_optimize].seconds()); + tty->print_cr (" Expand: %7.3f s", timers[_t_reachability_expand].seconds()); tty->print_cr (" AutoVectorize: %7.3f s", timers[_t_autoVectorize].seconds()); tty->print_cr (" IdealLoop Verify: %7.3f s", timers[_t_idealLoopVerify].seconds()); tty->print_cr (" Cond Const Prop: %7.3f s", timers[_t_ccp].seconds()); diff --git a/src/hotspot/share/opto/phase.hpp b/src/hotspot/share/opto/phase.hpp index 6700df6ec17..5bd3c34f15f 100644 --- a/src/hotspot/share/opto/phase.hpp +++ b/src/hotspot/share/opto/phase.hpp @@ -85,6 +85,9 @@ public: f( _t_vector_pru, "vector_pru") \ f( _t_renumberLive, "") \ f( _t_idealLoop, "idealLoop") \ + f( _t_reachability, "reachabilityFence") \ + f( _t_reachability_optimize, "reachabilityFence_optimize") \ + f( _t_reachability_expand, "reachabilityFence_expand") \ f( _t_autoVectorize, "autoVectorize") \ f( _t_idealLoopVerify, "idealLoopVerify") \ f( _t_ccp, "ccp") \ diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index 29961e152b3..a4d6a6c33d0 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -575,7 +575,7 @@ PhaseValues::~PhaseValues() { _table.dump(); // Statistics for value progress and efficiency if( PrintCompilation && Verbose && WizardMode ) { - tty->print("\n%sValues: %d nodes ---> %d/%d (%d)", + tty->print("\n%sValues: %d nodes ---> " UINT64_FORMAT "/%d (%d)", is_IterGVN() ? "Iter" : " ", C->unique(), made_progress(), made_transforms(), made_new_values()); if( made_transforms() != 0 ) { tty->print_cr(" ratio %f", made_progress()/(float)made_transforms() ); @@ -731,14 +731,14 @@ Node* PhaseGVN::transform(Node* n) { } if (t->singleton() && !k->is_Con()) { - NOT_PRODUCT(set_progress();) + set_progress(); return makecon(t); // Turn into a constant } // Now check for Identities i = k->Identity(this); // Look for a nearby replacement if (i != k) { // Found? Return replacement! - NOT_PRODUCT(set_progress();) + set_progress(); return i; } @@ -746,7 +746,7 @@ Node* PhaseGVN::transform(Node* n) { i = hash_find_insert(k); // Insert if new if (i && (i != k)) { // Return the pre-existing node - NOT_PRODUCT(set_progress();) + set_progress(); return i; } @@ -909,9 +909,9 @@ void PhaseIterGVN::verify_step(Node* n) { } } -void PhaseIterGVN::trace_PhaseIterGVN(Node* n, Node* nn, const Type* oldtype) { +void PhaseIterGVN::trace_PhaseIterGVN(Node* n, Node* nn, const Type* oldtype, bool progress) { const Type* newtype = type_or_null(n); - if (nn != n || oldtype != newtype) { + if (progress) { C->print_method(PHASE_AFTER_ITER_GVN_STEP, 5, n); } if (TraceIterativeGVN) { @@ -977,7 +977,7 @@ void PhaseIterGVN::init_verifyPhaseIterGVN() { #endif } -void PhaseIterGVN::verify_PhaseIterGVN() { +void PhaseIterGVN::verify_PhaseIterGVN(bool deep_revisit_converged) { #ifdef ASSERT // Verify nodes with changed inputs. Unique_Node_List* modified_list = C->modified_nodes(); @@ -1010,7 +1010,7 @@ void PhaseIterGVN::verify_PhaseIterGVN() { } } - verify_optimize(); + verify_optimize(deep_revisit_converged); #endif } #endif /* PRODUCT */ @@ -1040,8 +1040,155 @@ void PhaseIterGVN::trace_PhaseIterGVN_verbose(Node* n, int num_processed) { } #endif /* ASSERT */ -void PhaseIterGVN::optimize() { - DEBUG_ONLY(uint num_processed = 0;) +bool PhaseIterGVN::needs_deep_revisit(const Node* n) const { + // LoadNode::Value() -> can_see_stored_value() walks up through many memory + // nodes. LoadNode::Ideal() -> find_previous_store() also walks up to 50 + // nodes through stores and arraycopy nodes. + if (n->is_Load()) { + return true; + } + // CmpPNode::sub() -> detect_ptr_independence() -> all_controls_dominate() + // walks CFG dominator relationships extensively. This only triggers when + // both inputs are oop pointers (subnode.cpp:984). + if (n->Opcode() == Op_CmpP) { + const Type* t1 = type_or_null(n->in(1)); + const Type* t2 = type_or_null(n->in(2)); + return t1 != nullptr && t1->isa_oopptr() && + t2 != nullptr && t2->isa_oopptr(); + } + // IfNode::Ideal() -> search_identical() walks up the CFG dominator tree. + // RangeCheckNode::Ideal() scans up to ~999 nodes up the chain. + // CountedLoopEndNode/LongCountedLoopEndNode::Ideal() via simple_subsuming + // looks for dominating test that subsumes the current test. + switch (n->Opcode()) { + case Op_If: + case Op_RangeCheck: + case Op_CountedLoopEnd: + case Op_LongCountedLoopEnd: + return true; + default: + break; + } + return false; +} + +bool PhaseIterGVN::drain_worklist() { + uint loop_count = 1; + const int max_live_nodes_increase_per_iteration = NodeLimitFudgeFactor * 3; + while (_worklist.size() != 0) { + if (C->check_node_count(max_live_nodes_increase_per_iteration, "Out of nodes")) { + C->print_method(PHASE_AFTER_ITER_GVN, 3); + return true; + } + Node* n = _worklist.pop(); + if (loop_count >= K * C->live_nodes()) { + DEBUG_ONLY(dump_infinite_loop_info(n, "PhaseIterGVN::drain_worklist");) + C->record_method_not_compilable("infinite loop in PhaseIterGVN::drain_worklist"); + C->print_method(PHASE_AFTER_ITER_GVN, 3); + return true; + } + DEBUG_ONLY(trace_PhaseIterGVN_verbose(n, _num_processed++);) + if (n->outcnt() != 0) { + NOT_PRODUCT(const Type* oldtype = type_or_null(n)); + // Do the transformation + DEBUG_ONLY(int live_nodes_before = C->live_nodes();) + NOT_PRODUCT(uint progress_before = made_progress();) + Node* nn = transform_old(n); + NOT_PRODUCT(bool progress = (made_progress() - progress_before) > 0;) + DEBUG_ONLY(int live_nodes_after = C->live_nodes();) + // Ensure we did not increase the live node count with more than + // max_live_nodes_increase_per_iteration during the call to transform_old. + DEBUG_ONLY(int increase = live_nodes_after - live_nodes_before;) + assert(increase < max_live_nodes_increase_per_iteration, + "excessive live node increase in single iteration of IGVN: %d " + "(should be at most %d)", + increase, max_live_nodes_increase_per_iteration); + NOT_PRODUCT(trace_PhaseIterGVN(n, nn, oldtype, progress);) + } else if (!n->is_top()) { + remove_dead_node(n, NodeOrigin::Graph); + } + loop_count++; + } + return false; +} + +void PhaseIterGVN::push_deep_revisit_candidates() { + ResourceMark rm; + Unique_Node_List all_nodes; + all_nodes.push(C->root()); + for (uint j = 0; j < all_nodes.size(); j++) { + Node* n = all_nodes.at(j); + if (needs_deep_revisit(n)) { + _worklist.push(n); + } + for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { + all_nodes.push(n->fast_out(i)); + } + } +} + +bool PhaseIterGVN::deep_revisit() { + // Re-process nodes that inspect the graph deeply. After the main worklist drains, walk + // the graph to find all live deep-inspection nodes and push them to the worklist + // for re-evaluation. If any produce changes, drain the worklist again. + // Repeat until stable. This mirrors PhaseCCP::analyze()'s revisit loop. + const uint max_deep_revisit_rounds = 10; // typically converges in <2 rounds + uint round = 0; + for (; round < max_deep_revisit_rounds; round++) { + push_deep_revisit_candidates(); + if (_worklist.size() == 0) { + break; // No deep-inspection nodes to revisit, done. + } + +#ifndef PRODUCT + uint candidates = _worklist.size(); + uint n_if = 0; uint n_rc = 0; uint n_load = 0; uint n_cmpp = 0; uint n_cle = 0; uint n_lcle = 0; + if (TraceIterativeGVN) { + for (uint i = 0; i < _worklist.size(); i++) { + Node* n = _worklist.at(i); + switch (n->Opcode()) { + case Op_If: n_if++; break; + case Op_RangeCheck: n_rc++; break; + case Op_CountedLoopEnd: n_cle++; break; + case Op_LongCountedLoopEnd: n_lcle++; break; + case Op_CmpP: n_cmpp++; break; + default: if (n->is_Load()) n_load++; break; + } + } + } +#endif + + // Convergence: if the drain does not make progress (no Ideal, Value, Identity or GVN changes), + // we are at a fixed point. We use made_progress() rather than live_nodes because live_nodes + // misses non-structural changes like a LoadNode dropping its control input. + uint progress_before = made_progress(); + if (drain_worklist()) { + return false; + } + uint progress = made_progress() - progress_before; + +#ifndef PRODUCT + if (TraceIterativeGVN) { + tty->print("deep_revisit round %u: %u candidates (If=%u RC=%u Load=%u CmpP=%u CLE=%u LCLE=%u), progress=%u (%s)", + round, candidates, n_if, n_rc, n_load, n_cmpp, n_cle, n_lcle, progress, progress != 0 ? "changed" : "converged"); + if (C->method() != nullptr) { + tty->print(", "); + C->method()->print_short_name(tty); + } + tty->cr(); + } +#endif + + if (progress == 0) { + break; + } + } + return round < max_deep_revisit_rounds; +} + +void PhaseIterGVN::optimize(bool deep) { + bool deep_revisit_converged = false; + DEBUG_ONLY(_num_processed = 0;) NOT_PRODUCT(init_verifyPhaseIterGVN();) NOT_PRODUCT(C->reset_igv_phase_iter(PHASE_AFTER_ITER_GVN_STEP);) C->print_method(PHASE_BEFORE_ITER_GVN, 3); @@ -1049,54 +1196,24 @@ void PhaseIterGVN::optimize() { shuffle_worklist(); } - // The node count check in the loop below (check_node_count) assumes that we - // increase the live node count with at most - // max_live_nodes_increase_per_iteration in between checks. If this - // assumption does not hold, there is a risk that we exceed the max node - // limit in between checks and trigger an assert during node creation. - const int max_live_nodes_increase_per_iteration = NodeLimitFudgeFactor * 3; - - uint loop_count = 0; - // Pull from worklist and transform the node. If the node has changed, - // update edge info and put uses on worklist. - while (_worklist.size() > 0) { - if (C->check_node_count(max_live_nodes_increase_per_iteration, "Out of nodes")) { - C->print_method(PHASE_AFTER_ITER_GVN, 3); - return; - } - Node* n = _worklist.pop(); - if (loop_count >= K * C->live_nodes()) { - DEBUG_ONLY(dump_infinite_loop_info(n, "PhaseIterGVN::optimize");) - C->record_method_not_compilable("infinite loop in PhaseIterGVN::optimize"); - C->print_method(PHASE_AFTER_ITER_GVN, 3); - return; - } - DEBUG_ONLY(trace_PhaseIterGVN_verbose(n, num_processed++);) - if (n->outcnt() != 0) { - NOT_PRODUCT(const Type* oldtype = type_or_null(n)); - // Do the transformation - DEBUG_ONLY(int live_nodes_before = C->live_nodes();) - Node* nn = transform_old(n); - DEBUG_ONLY(int live_nodes_after = C->live_nodes();) - // Ensure we did not increase the live node count with more than - // max_live_nodes_increase_per_iteration during the call to transform_old - DEBUG_ONLY(int increase = live_nodes_after - live_nodes_before;) - assert(increase < max_live_nodes_increase_per_iteration, - "excessive live node increase in single iteration of IGVN: %d " - "(should be at most %d)", - increase, max_live_nodes_increase_per_iteration); - NOT_PRODUCT(trace_PhaseIterGVN(n, nn, oldtype);) - } else if (!n->is_top()) { - remove_dead_node(n); - } - loop_count++; + // Pull from worklist and transform the node. + if (drain_worklist()) { + return; } - NOT_PRODUCT(verify_PhaseIterGVN();) + + if (deep && UseDeepIGVNRevisit) { + deep_revisit_converged = deep_revisit(); + if (C->failing()) { + return; + } + } + + NOT_PRODUCT(verify_PhaseIterGVN(deep_revisit_converged);) C->print_method(PHASE_AFTER_ITER_GVN, 3); } #ifdef ASSERT -void PhaseIterGVN::verify_optimize() { +void PhaseIterGVN::verify_optimize(bool deep_revisit_converged) { assert(_worklist.size() == 0, "igvn worklist must be empty before verify"); if (is_verify_Value() || @@ -1114,11 +1231,11 @@ void PhaseIterGVN::verify_optimize() { // in PhaseIterGVN::add_users_to_worklist to update it again or add an exception // in the verification methods below if that is not possible for some reason (like Load nodes). if (is_verify_Value()) { - verify_Value_for(n); + verify_Value_for(n, deep_revisit_converged /* strict */); } if (is_verify_Ideal()) { - verify_Ideal_for(n, false); - verify_Ideal_for(n, true); + verify_Ideal_for(n, false /* can_reshape */, deep_revisit_converged); + verify_Ideal_for(n, true /* can_reshape */, deep_revisit_converged); } if (is_verify_Identity()) { verify_Identity_for(n); @@ -1240,52 +1357,15 @@ void PhaseIterGVN::verify_Value_for(const Node* n, bool strict) { // Check that all Ideal optimizations that could be done were done. // Asserts if it found missed optimization opportunities or encountered unexpected changes, and // returns normally otherwise (no missed optimization, or skipped verification). -void PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { +void PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape, bool deep_revisit_converged) { + if (!deep_revisit_converged && needs_deep_revisit(n)) { + return; + } + // First, we check a list of exceptions, where we skip verification, // because there are known cases where Ideal can optimize after IGVN. // Some may be expected and cannot be fixed, and others should be fixed. switch (n->Opcode()) { - // RangeCheckNode::Ideal looks up the chain for about 999 nodes - // (see "Range-Check scan limit"). So, it is possible that something - // is optimized in that input subgraph, and the RangeCheck was not - // added to the worklist because it would be too expensive to walk - // down the graph for 1000 nodes and put all on the worklist. - // - // Found with: - // java -XX:VerifyIterativeGVN=0100 -Xbatch --version - case Op_RangeCheck: - return; - - // IfNode::Ideal does: - // Node* prev_dom = search_identical(dist, igvn); - // which means we seach up the CFG, traversing at most up to a distance. - // If anything happens rather far away from the If, we may not put the If - // back on the worklist. - // - // Found with: - // java -XX:VerifyIterativeGVN=0100 -Xcomp --version - case Op_If: - return; - - // IfNode::simple_subsuming - // Looks for dominating test that subsumes the current test. - // Notification could be difficult because of larger distance. - // - // Found with: - // runtime/exceptionMsgs/ArrayIndexOutOfBoundsException/ArrayIndexOutOfBoundsExceptionTest.java#id1 - // -XX:VerifyIterativeGVN=1110 - case Op_CountedLoopEnd: - return; - - // LongCountedLoopEndNode::Ideal - // Probably same issue as above. - // - // Found with: - // compiler/predicates/assertion/TestAssertionPredicates.java#NoLoopPredicationXbatch - // -XX:StressLongCountedLoop=2000000 -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110 - case Op_LongCountedLoopEnd: - return; - // RegionNode::Ideal does "Skip around the useless IF diamond". // 245 IfTrue === 244 // 258 If === 245 257 @@ -1757,22 +1837,6 @@ void PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { return; } - if (n->is_Load()) { - // LoadNode::Ideal uses tries to find an earlier memory state, and - // checks can_see_stored_value for it. - // - // Investigate why this was not already done during IGVN. - // A similar issue happens with Identity. - // - // There seem to be other cases where loads go up some steps, like - // LoadNode::Ideal going up 10x steps to find dominating load. - // - // Found with: - // test/hotspot/jtreg/compiler/arraycopy/TestCloneAccess.java - // -XX:VerifyIterativeGVN=1110 - return; - } - if (n->is_Store()) { // StoreNode::Ideal can do this: // // Capture an unaliased, unconditional, simple store into an initializer. @@ -1857,8 +1921,16 @@ void PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { return; } - // The number of nodes shoud not increase. - uint old_unique = C->unique(); + // Ideal should not make progress if it returns nullptr. + // We use made_progress() rather than unique() or live_nodes() because some + // Ideal implementations speculatively create nodes and kill them before + // returning nullptr (e.g. split_if clones a Cmp to check is_canonical). + // unique() is a high-water mark that is not decremented by remove_dead_node, + // so it would cause false-positives. live_nodes() accounts for dead nodes but can + // decrease when Ideal removes existing nodes as side effects. + // made_progress() precisely tracks meaningful transforms, and speculative + // work killed via NodeOrigin::Speculative does not increment it. + uint old_progress = made_progress(); // The hash of a node should not change, this would indicate different inputs uint old_hash = n->hash(); // Remove 'n' from hash table in case it gets modified. We want to avoid @@ -1870,14 +1942,15 @@ void PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { Node* i = n->Ideal(this, can_reshape); // If there was no new Idealization, we are probably happy. if (i == nullptr) { - if (old_unique < C->unique()) { + uint progress = made_progress() - old_progress; + if (progress != 0) { stringStream ss; // Print as a block without tty lock. ss.cr(); - ss.print_cr("Ideal optimization did not make progress but created new unused nodes."); - ss.print_cr(" old_unique = %d, unique = %d", old_unique, C->unique()); + ss.print_cr("Ideal optimization did not make progress but had side effects."); + ss.print_cr(" %u transforms made progress", progress); n->dump_bfs(1, nullptr, "", &ss); tty->print_cr("%s", ss.as_string()); - assert(false, "Unexpected new unused nodes from applying Ideal optimization on %s", n->Name()); + assert(false, "Unexpected side effects from applying Ideal optimization on %s", n->Name()); } if (old_hash != n->hash()) { @@ -2050,7 +2123,12 @@ void PhaseIterGVN::verify_Identity_for(Node* n) { if (n->is_Vector()) { // Found with tier1-3. Not investigated yet. - // The observed issue was with AndVNode::Identity + // The observed issue was with AndVNode::Identity and + // VectorStoreMaskNode::Identity (see JDK-8370863). + // + // Found with: + // compiler/vectorapi/VectorStoreMaskIdentityTest.java + // -XX:CompileThreshold=100 -XX:-TieredCompilation -XX:VerifyIterativeGVN=1110 return; } @@ -2152,6 +2230,9 @@ Node *PhaseIterGVN::transform_old(Node* n) { #endif DEBUG_ONLY(uint loop_count = 1;) + if (i != nullptr) { + set_progress(); + } while (i != nullptr) { #ifdef ASSERT if (loop_count >= K + C->live_nodes()) { @@ -2197,10 +2278,8 @@ Node *PhaseIterGVN::transform_old(Node* n) { // cache Value. Later requests for the local phase->type of this Node can // use the cached Value instead of suffering with 'bottom_type'. if (type_or_null(k) != t) { -#ifndef PRODUCT - inc_new_values(); + NOT_PRODUCT(inc_new_values();) set_progress(); -#endif set_type(k, t); // If k is a TypeNode, capture any more-precise type permanently into Node k->raise_bottom_type(t); @@ -2209,7 +2288,7 @@ Node *PhaseIterGVN::transform_old(Node* n) { } // If 'k' computes a constant, replace it with a constant if (t->singleton() && !k->is_Con()) { - NOT_PRODUCT(set_progress();) + set_progress(); Node* con = makecon(t); // Make a constant add_users_to_worklist(k); subsume_node(k, con); // Everybody using k now uses con @@ -2219,7 +2298,7 @@ Node *PhaseIterGVN::transform_old(Node* n) { // Now check for Identities i = k->Identity(this); // Look for a nearby replacement if (i != k) { // Found? Return replacement! - NOT_PRODUCT(set_progress();) + set_progress(); add_users_to_worklist(k); subsume_node(k, i); // Everybody using k now uses i return i; @@ -2229,7 +2308,7 @@ Node *PhaseIterGVN::transform_old(Node* n) { i = hash_find_insert(k); // Check for pre-existing node if (i && (i != k)) { // Return the pre-existing node if it isn't dead - NOT_PRODUCT(set_progress();) + set_progress(); add_users_to_worklist(k); subsume_node(k, i); // Everybody using k now uses i return i; @@ -2248,7 +2327,7 @@ const Type* PhaseIterGVN::saturate(const Type* new_type, const Type* old_type, //------------------------------remove_globally_dead_node---------------------- // Kill a globally dead Node. All uses are also globally dead and are // aggressively trimmed. -void PhaseIterGVN::remove_globally_dead_node( Node *dead ) { +void PhaseIterGVN::remove_globally_dead_node(Node* dead, NodeOrigin origin) { enum DeleteProgress { PROCESS_INPUTS, PROCESS_OUTPUTS @@ -2265,11 +2344,13 @@ void PhaseIterGVN::remove_globally_dead_node( Node *dead ) { uint progress_state = stack.index(); assert(dead != C->root(), "killing root, eh?"); assert(!dead->is_top(), "add check for top when pushing"); - NOT_PRODUCT( set_progress(); ) if (progress_state == PROCESS_INPUTS) { // After following inputs, continue to outputs stack.set_index(PROCESS_OUTPUTS); if (!dead->is_Con()) { // Don't kill cons but uses + if (origin != NodeOrigin::Speculative) { + set_progress(); + } bool recurse = false; // Remove from hash table _table.hash_delete( dead ); @@ -2379,7 +2460,7 @@ void PhaseIterGVN::subsume_node( Node *old, Node *nn ) { // Smash all inputs to 'old', isolating him completely Node *temp = new Node(1); temp->init_req(0,nn); // Add a use to nn to prevent him from dying - remove_dead_node( old ); + remove_dead_node(old, NodeOrigin::Graph); temp->del_req(0); // Yank bogus edge if (nn != nullptr && nn->outcnt() == 0) { _worklist.push(nn); diff --git a/src/hotspot/share/opto/phaseX.hpp b/src/hotspot/share/opto/phaseX.hpp index 94890c250c4..014d16f92f6 100644 --- a/src/hotspot/share/opto/phaseX.hpp +++ b/src/hotspot/share/opto/phaseX.hpp @@ -187,8 +187,8 @@ public: class PhaseTransform : public Phase { public: PhaseTransform(PhaseNumber pnum) : Phase(pnum) { -#ifndef PRODUCT clear_progress(); +#ifndef PRODUCT clear_transforms(); set_allow_progress(true); #endif @@ -201,12 +201,31 @@ public: // true if CFG node d dominates CFG node n virtual bool is_dominator(Node *d, Node *n) { fatal("unimplemented for this pass"); return false; }; -#ifndef PRODUCT - uint _count_progress; // For profiling, count transforms that make progress - void set_progress() { ++_count_progress; assert( allow_progress(),"No progress allowed during verification"); } - void clear_progress() { _count_progress = 0; } - uint made_progress() const { return _count_progress; } + uint64_t _count_progress; // Count transforms that make progress + void set_progress() { ++_count_progress; assert(allow_progress(), "No progress allowed during verification"); } + void clear_progress() { _count_progress = 0; } + uint64_t made_progress() const { return _count_progress; } + // RAII guard for speculative transforms. Restores _count_progress in the destructor + // unless commit() is called, so that abandoned speculative work does not count as progress. + // In case multiple nodes are created and only some are speculative, commit() should still be called. + class SpeculativeProgressGuard { + PhaseTransform* _phase; + uint64_t _saved_progress; + bool _committed; + public: + SpeculativeProgressGuard(PhaseTransform* phase) : + _phase(phase), _saved_progress(phase->made_progress()), _committed(false) {} + ~SpeculativeProgressGuard() { + if (!_committed) { + _phase->_count_progress = _saved_progress; + } + } + + void commit() { _committed = true; } + }; + +#ifndef PRODUCT uint _count_transforms; // For profiling, count transforms performed void set_transforms() { ++_count_transforms; } void clear_transforms() { _count_transforms = 0; } @@ -446,10 +465,30 @@ class PhaseIterGVN : public PhaseGVN { private: bool _delay_transform; // When true simply register the node when calling transform // instead of actually optimizing it + DEBUG_ONLY(uint _num_processed;) // Running count for trace_PhaseIterGVN_verbose // Idealize old Node 'n' with respect to its inputs and its value virtual Node *transform_old( Node *a_node ); + // Drain the IGVN worklist: process nodes until the worklist is empty. + // Returns true if compilation was aborted (node limit or infinite loop), + // false on normal completion. + bool drain_worklist(); + + // Walk all live nodes and push deep-inspection candidates to _worklist. + void push_deep_revisit_candidates(); + + // After the main worklist drains, re-process deep-inspection nodes to + // catch optimization opportunities from far-away changes. Repeats until + // convergence (no progress made) or max rounds reached. + // Returns true if converged. + bool deep_revisit(); + + // Returns true for nodes that inspect the graph beyond their direct + // inputs, and therefore may miss optimization opportunities when + // changes happen far away. + bool needs_deep_revisit(const Node* n) const; + // Subsume users of node 'old' into node 'nn' void subsume_node( Node *old, Node *nn ); @@ -493,20 +532,25 @@ public: // Given def-use info and an initial worklist, apply Node::Ideal, // Node::Value, Node::Identity, hash-based value numbering, Node::Ideal_DU // and dominator info to a fixed point. - void optimize(); + // When deep is true, after the main worklist drains, re-process + // nodes that inspect the graph deeply (Load, CmpP, If, RangeCheck, + // CountedLoopEnd, LongCountedLoopEnd) to catch optimization opportunities + // from changes far away that the normal notification mechanism misses. + void optimize(bool deep = false); + #ifdef ASSERT - void verify_optimize(); + void verify_optimize(bool deep_revisit_converged); void verify_Value_for(const Node* n, bool strict = false); - void verify_Ideal_for(Node* n, bool can_reshape); + void verify_Ideal_for(Node* n, bool can_reshape, bool deep_revisit_converged); void verify_Identity_for(Node* n); void verify_node_invariants_for(const Node* n); void verify_empty_worklist(Node* n); #endif #ifndef PRODUCT - void trace_PhaseIterGVN(Node* n, Node* nn, const Type* old_type); + void trace_PhaseIterGVN(Node* n, Node* nn, const Type* old_type, bool progress); void init_verifyPhaseIterGVN(); - void verify_PhaseIterGVN(); + void verify_PhaseIterGVN(bool deep_revisit_converged); #endif #ifdef ASSERT @@ -522,15 +566,21 @@ public: // It is significant only for debugging and profiling. Node* register_new_node_with_optimizer(Node* n, Node* orig = nullptr); - // Kill a globally dead Node. All uses are also globally dead and are + // Origin of a dead node, describing why it is dying. + // Speculative: a temporarily created node that was never part of the graph + // (e.g., a speculative clone in split_if to test constant foldability). + // Its death does not count as progress for convergence tracking. + enum class NodeOrigin { Graph, Speculative }; + + // Kill a globally dead Node. All uses are also globally dead and are // aggressively trimmed. - void remove_globally_dead_node( Node *dead ); + void remove_globally_dead_node(Node* dead, NodeOrigin origin); // Kill all inputs to a dead node, recursively making more dead nodes. // The Node must be dead locally, i.e., have no uses. - void remove_dead_node( Node *dead ) { + void remove_dead_node(Node* dead, NodeOrigin origin) { assert(dead->outcnt() == 0 && !dead->is_top(), "node must be dead"); - remove_globally_dead_node(dead); + remove_globally_dead_node(dead, origin); } // Add users of 'n' to worklist diff --git a/src/hotspot/share/opto/phasetype.hpp b/src/hotspot/share/opto/phasetype.hpp index ce432fbfc01..3d316112b2d 100644 --- a/src/hotspot/share/opto/phasetype.hpp +++ b/src/hotspot/share/opto/phasetype.hpp @@ -106,6 +106,7 @@ flags(PHASEIDEALLOOP1, "PhaseIdealLoop 1") \ flags(PHASEIDEALLOOP2, "PhaseIdealLoop 2") \ flags(PHASEIDEALLOOP3, "PhaseIdealLoop 3") \ + flags(EXPAND_REACHABILITY_FENCES, "Expand Reachability Fences") \ flags(AUTO_VECTORIZATION1_BEFORE_APPLY, "AutoVectorization 1, before 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") \ @@ -203,7 +204,7 @@ class PhaseNameValidator { ~PhaseNameValidator() { if (_bad != nullptr) { - FREE_C_HEAP_ARRAY(char, _bad); + FREE_C_HEAP_ARRAY(_bad); } } diff --git a/src/hotspot/share/opto/reachability.cpp b/src/hotspot/share/opto/reachability.cpp new file mode 100644 index 00000000000..b45b340ab85 --- /dev/null +++ b/src/hotspot/share/opto/reachability.cpp @@ -0,0 +1,512 @@ +/* + * Copyright (c) 2026, 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 "opto/c2_MacroAssembler.hpp" +#include "opto/callnode.hpp" +#include "opto/compile.hpp" +#include "opto/loopnode.hpp" +#include "opto/phaseX.hpp" +#include "opto/reachability.hpp" +#include "opto/regalloc.hpp" +#include "opto/runtime.hpp" +#include "utilities/pair.hpp" + +/* + * java.lang.ref.Reference::reachabilityFence support. + * + * Reachability Fence (RF) ensures that the given object (referent) remains strongly reachable + * regardless of any optimizing transformations the virtual machine may perform that might otherwise + * allow the object to become unreachable. + * + * RFs are intended to be used in performance-critical code, so the primary goal for C2 support is + * to reduce their runtime overhead as much as possible. + * + * Reference::reachabilityFence() calls are intrinsified into ReachabilityFence CFG nodes. RF node keeps + * its referent alive, so the referent's location is recorded at every safepoint (in its oop map) which + * interferes with referent's live range. + * + * It is tempting to directly attach referents to interfering safepoints right from the beginning, but it + * doesn't play well with some optimizations C2 does (e.g., during loop-invariant code motion a safepoint + * can become interfering once a load is hoisted). + * + * Instead, reachability representation transitions through multiple phases: + * (0) initial set of RFs is materialized during parsing (as a result of + * Reference.reachabilityFence intrinsification); + * (1) optimization pass during loop opts eliminates redundant RF nodes and + * moves the ones with loop-invariant referents outside (after) loops; + * (2) after loop opts are over, RF nodes are eliminated and their referents are transferred to + * safepoint nodes (appended as edges after debug info); + * (3) during final graph reshaping, referent edges are removed from safepoints and materialized as RF nodes + * attached to their safepoint node (closely following it in CFG graph). + * + * Some implementation considerations. + * + * (a) It looks attractive to get rid of RF nodes early and transfer to safepoint-attached representation, + * but it is not correct until loop opts are done. + * + * Live ranges of values are routinely extended during loop opts. And it can break the invariant that + * all interfering safepoints contain the referent in their oop map. (If an interfering safepoint doesn't + * keep the referent alive, then it becomes possible for the referent to be prematurely GCed.) + * + * compiler/c2/TestReachabilityFence.java demonstrates a situation where a load is hoisted out of a loop thus + * extending the live range of the value it produces beyond the safepoint on loop-back edge. + * + * After loop opts are over, it becomes possible to reliably enumerate all interfering safepoints and + * to ensure that the referent is present in their oop maps. Current assumption is that after loop opts the IR graph + * is stable enough, so relative order of memory operations and safepoints is preserved and only safepoints between + * a referent and it's uses are taken into account. A more conservative analysis can be employed -- any safepoint dominated + * by a referent is treated as interfering with it -- if it turns out that the assumption doesn't hold. + * + * (b) RF nodes may interfere with Register Allocator (RA). If a safepoint is pruned during macro expansion, + * it can make some RF nodes redundant, but we don't have information about their relations anymore to detect that. + * Redundant RF node unnecessarily extends referent's live range and increases register pressure. + * + * Hence, we eliminate RF nodes and transfer their referents to corresponding safepoints (phase #2). + * When safepoints are pruned, corresponding reachability edges also go away. + * + * (c) Unfortunately, it's not straightforward to stay with safepoint-attached representation till the very end, + * because information about derived oops is attached to safepoints in a similar way. So, for now RFs are + * rematerialized at safepoints before RA (phase #3). + */ + +bool ReachabilityFenceNode::is_redundant(PhaseGVN& gvn) { + const Type* referent_t = gvn.type(referent()); + if (referent_t == TypePtr::NULL_PTR) { + return true; // no-op fence: null referent + } + if (!OptimizeReachabilityFences) { + return false; // keep reachability fence nodes intact + } + if (!PreserveReachabilityFencesOnConstants && referent_t->singleton()) { + return true; // no-op fence: constants are strongly reachable + } + return false; +} + +Node* ReachabilityFenceNode::Ideal(PhaseGVN* phase, bool can_reshape) { + return (remove_dead_region(phase, can_reshape) ? this : nullptr); +} + +Node* ReachabilityFenceNode::Identity(PhaseGVN* phase) { + if (is_redundant(*phase)) { + return in(0); + } + return this; +} + +// Turn the RF node into a no-op by setting its referent to null. +// Subsequent IGVN pass removes cleared nodes. +bool ReachabilityFenceNode::clear_referent(PhaseIterGVN& phase) { + if (phase.type(referent()) == TypePtr::NULL_PTR) { + return false; + } else { + phase.replace_input_of(this, 1, phase.makecon(TypePtr::NULL_PTR)); + return true; + } +} + +#ifndef PRODUCT +static void rf_desc(outputStream* st, const ReachabilityFenceNode* rf, PhaseRegAlloc* ra) { + char buf[50]; + ra->dump_register(rf->referent(), buf, sizeof(buf)); + st->print("reachability fence [%s]", buf); +} + +void ReachabilityFenceNode::format(PhaseRegAlloc* ra, outputStream* st) const { + rf_desc(st, this, ra); +} + +void ReachabilityFenceNode::emit(C2_MacroAssembler* masm, PhaseRegAlloc* ra) const { + ResourceMark rm; + stringStream ss; + rf_desc(&ss, this, ra); + const char* desc = masm->code_string(ss.freeze()); + masm->block_comment(desc); +} +#endif + +// Detect safepoint nodes which are important for reachability tracking purposes. +// Some SafePoint nodes can't affect referent's reachability in any noticeable way and +// can be safely ignored during the analysis. +static bool is_interfering_sfpt_candidate(SafePointNode* sfpt) { + if (sfpt->jvms() == nullptr) { + return false; // not a real safepoint + } else if (sfpt->is_CallStaticJava() && sfpt->as_CallStaticJava()->is_uncommon_trap()) { + return false; // uncommon traps are exit points + } + return true; // a full-blown safepoint can interfere with a reachability fence and its referent +} + +void PhaseIdealLoop::insert_rf(Node* ctrl, Node* referent) { + IdealLoopTree* lpt = get_loop(ctrl); + Node* ctrl_end = ctrl->unique_ctrl_out(); + + auto new_rf = new ReachabilityFenceNode(C, ctrl, referent); + + register_control(new_rf, lpt, ctrl); + set_idom(new_rf, ctrl, dom_depth(ctrl) + 1); + lpt->register_reachability_fence(new_rf); + + igvn().rehash_node_delayed(ctrl_end); + ctrl_end->replace_edge(ctrl, new_rf); + + if (idom(ctrl_end) == ctrl) { + set_idom(ctrl_end, new_rf, dom_depth(new_rf) + 1); + } else { + assert(ctrl_end->is_Region(), ""); + } +} + +void PhaseIdealLoop::replace_rf(Node* old_node, Node* new_node) { + assert(old_node->is_ReachabilityFence() || + (old_node->is_Proj() && old_node->in(0)->is_ReachabilityFence()), + "%s", NodeClassNames[old_node->Opcode()]); + + IdealLoopTree* lpt = get_loop(old_node); + lpt->_body.yank(old_node); + assert(lpt->_reachability_fences != nullptr, "missing"); + assert(lpt->_reachability_fences->contains(old_node), "missing"); + lpt->_reachability_fences->yank(old_node); + replace_node_and_forward_ctrl(old_node, new_node); +} + +void PhaseIdealLoop::remove_rf(ReachabilityFenceNode* rf) { + Node* rf_ctrl_in = rf->in(0); + Node* referent = rf->referent(); + if (rf->clear_referent(igvn()) && referent->outcnt() == 0) { + remove_dead_data_node(referent); + } + replace_rf(rf, rf_ctrl_in); +} + +//====================================================================== +//---------------------------- Phase 1 --------------------------------- +// Optimization pass over reachability fences during loop opts. +// Moves RFs with loop-invariant referents out of the loop. +bool PhaseIdealLoop::optimize_reachability_fences() { + Compile::TracePhase tp(_t_reachability_optimize); + + assert(OptimizeReachabilityFences, "required"); + + // ResourceMark rm; // NB! not safe because insert_rf may trigger _idom reallocation + Unique_Node_List redundant_rfs; + typedef Pair LoopExit; + GrowableArray worklist; + + for (int i = 0; i < C->reachability_fences_count(); i++) { + ReachabilityFenceNode* rf = C->reachability_fence(i); + assert(!rf->is_redundant(igvn()), "required"); + // Move RFs out of counted loops when possible. + IdealLoopTree* lpt = get_loop(rf); + Node* referent = rf->referent(); + if (lpt->is_invariant(referent)) { + IfFalseNode* unique_loop_exit = lpt->unique_loop_exit_proj_or_null(); + if (unique_loop_exit != nullptr) { + // Skip over an outer strip-mined loop. + if (!lpt->is_root()) { + IdealLoopTree* outer_lpt = lpt->_parent; + if (outer_lpt->head()->is_OuterStripMinedLoop()) { + if (outer_lpt->is_invariant(referent)) { + IfNode* outer_loop_end = outer_lpt->head()->as_OuterStripMinedLoop()->outer_loop_end(); + if (outer_loop_end != nullptr && outer_loop_end->false_proj_or_null() != nullptr) { + unique_loop_exit = outer_loop_end->false_proj_or_null(); + } + } else { + // An attempt to insert an RF node inside outer strip-mined loop breaks + // its IR invariants and manifests as assertion failures. + assert(false, "not loop invariant in outer strip-mined loop"); + continue; // skip + } + } + } + + LoopExit p(referent, unique_loop_exit); + worklist.push(p); + redundant_rfs.push(rf); + +#ifndef PRODUCT + if (TraceLoopOpts) { + IdealLoopTree* loop = get_loop(unique_loop_exit->in(0)); + tty->print_cr("ReachabilityFence: N%d: %s N%d/N%d -> loop exit N%d (%s N%d/N%d)", + rf->_idx, lpt->head()->Name(), lpt->head()->_idx, lpt->tail()->_idx, + unique_loop_exit->_idx, + loop->head()->Name(), loop->head()->_idx, loop->tail()->_idx); + } +#endif // !PRODUCT + } + } + } + + // Populate RFs outside loops. + while (worklist.is_nonempty()) { + LoopExit p = worklist.pop(); + Node* referent = p.first; + Node* ctrl_out = p.second; + insert_rf(ctrl_out, referent); + } + + // Eliminate redundant RFs. + bool progress = (redundant_rfs.size() > 0); + while (redundant_rfs.size() > 0) { + remove_rf(redundant_rfs.pop()->as_ReachabilityFence()); + } + + return progress; +} + +//====================================================================== +//---------------------------- Phase 2 --------------------------------- + +// Linearly traverse CFG upwards starting at ctrl_start until first merge point. +// All encountered safepoints are recorded in safepoints list, except +// the ones filtered out by is_interfering_sfpt_candidate(). +static void enumerate_interfering_sfpts_linear_traversal(Node* ctrl_start, Node_Stack& stack, VectorSet& visited, + Node_List& interfering_sfpts) { + for (Node* ctrl = ctrl_start; ctrl != nullptr; ctrl = ctrl->in(0)) { + assert(ctrl->is_CFG(), ""); + if (visited.test_set(ctrl->_idx)) { + return; + } else { + if (ctrl->is_Region()) { + stack.push(ctrl, 1); + return; // stop at merge points + } else if (ctrl->is_SafePoint() && is_interfering_sfpt_candidate(ctrl->as_SafePoint())) { + assert(!ctrl->is_CallStaticJava() || !ctrl->as_CallStaticJava()->is_uncommon_trap(), + "uncommon traps should not be enumerated"); + interfering_sfpts.push(ctrl); + } + } + } +} + +// Enumerate all safepoints which are reachable from the RF to its referent through CFG. +// Start at RF node and traverse CFG upwards until referent's control node is reached. +static void enumerate_interfering_sfpts(ReachabilityFenceNode* rf, PhaseIdealLoop* phase, + Node_Stack& stack, VectorSet& visited, + Node_List& interfering_sfpts) { + assert(stack.is_empty(), "required"); + assert(visited.is_empty(), "required"); + + Node* referent = rf->referent(); + Node* referent_ctrl = phase->get_ctrl(referent); + assert(phase->is_dominator(referent_ctrl, rf), "sanity"); + + visited.set(referent_ctrl->_idx); // end point + enumerate_interfering_sfpts_linear_traversal(rf, stack, visited, interfering_sfpts); // starting point in CFG + while (stack.is_nonempty()) { + Node* cur = stack.node(); + uint idx = stack.index(); + + assert(cur != nullptr, ""); + assert(cur->is_Region(), "%s", NodeClassNames[cur->Opcode()]); + assert(phase->is_dominator(referent_ctrl, cur), ""); + assert(idx > 0 && idx <= cur->req(), "%d %d", idx, cur->req()); + + if (idx < cur->req()) { + stack.set_index(idx + 1); + enumerate_interfering_sfpts_linear_traversal(cur->in(idx), stack, visited, interfering_sfpts); + } else { + stack.pop(); + } + } + // Reset temporary structures to their initial state. + assert(stack.is_empty(), "required"); + visited.clear(); +} + +// Start offset for reachability info on a safepoint node. +static uint rf_base_offset(SafePointNode* sfpt) { + return sfpt->jvms()->debug_end(); +} + +static bool dominates_another_rf(ReachabilityFenceNode* rf, PhaseIdealLoop* phase) { + assert(!rf->is_redundant(phase->igvn()), ""); + + for (int i = 0; i < phase->C->reachability_fences_count(); i++) { + ReachabilityFenceNode* other_rf = phase->C->reachability_fence(i); + assert(other_rf->outcnt() > 0, "dead node"); + if (rf != other_rf && rf->referent()->eqv_uncast(other_rf->referent()) && + phase->is_dominator(rf, other_rf)) { + return true; // dominates another reachability fence with the same referent + } + } + return false; +} + +// Phase 2: migrate reachability info to safepoints. +// All RFs are replaced with edges from corresponding referents to interfering safepoints. +// Interfering safepoints are safepoint nodes which are reachable from the RF to its referent through CFG. +bool PhaseIdealLoop::expand_reachability_fences() { + Compile::TracePhase tp(_t_reachability_expand); + + assert(OptimizeReachabilityFences, "required"); + assert(C->post_loop_opts_phase(), "required"); + DEBUG_ONLY( int no_of_constant_rfs = 0; ) + + ResourceMark rm; + Unique_Node_List for_removal; + typedef Pair ReachabilityEdge; + GrowableArray reachability_edges; + { + // Reuse temporary structures to avoid allocating them for every single RF node. + Node_List sfpt_worklist; + Node_Stack stack(0); + VectorSet visited; + + for (int i = 0; i < C->reachability_fences_count(); i++) { + ReachabilityFenceNode* rf = C->reachability_fence(i); + assert(!rf->is_redundant(igvn()), "missed"); + if (PreserveReachabilityFencesOnConstants) { + const Type* referent_t = igvn().type(rf->referent()); + assert(referent_t != TypePtr::NULL_PTR, "redundant rf"); + bool is_constant_rf = referent_t->singleton(); + if (is_constant_rf) { + DEBUG_ONLY( no_of_constant_rfs += 1; ) + continue; // leave RFs on constants intact + } + } + if (dominates_another_rf(rf, this)) { + // Redundant fence: dominates another RF with the same referent. + // RF is redundant for some referent oop when the referent has another RF which + // keeps it alive across the RF. In terms of dominance relation it can be formulated + // as "a referent has another RF which is dominated by the redundant RF". + // + // NB! It is safe to assume that dominating RF is redundant only during expansion. + // Otherwise, there's a chance for the superseding RF node to go away before expansion. + // Non-RF users are ignored for a similar reason: they can go away before or after expansion + // takes place, so no guarantees reachability information is preserved. + } else { + assert(sfpt_worklist.size() == 0, "not empty"); + enumerate_interfering_sfpts(rf, this, stack, visited, sfpt_worklist); + + Node* referent = rf->referent(); + while (sfpt_worklist.size() > 0) { + SafePointNode* sfpt = sfpt_worklist.pop()->as_SafePoint(); + assert(is_dominator(get_ctrl(referent), sfpt), ""); + assert(sfpt->req() == rf_base_offset(sfpt), "no extra edges allowed"); + if (sfpt->find_edge(referent) == -1) { + ReachabilityEdge p(sfpt, referent); + reachability_edges.push(p); + } + } + } + for_removal.push(rf); + } + } + // Materialize reachability edges. + while (reachability_edges.length() > 0) { + ReachabilityEdge p = reachability_edges.pop(); + SafePointNode* sfpt = p.first; + Node* referent = p.second; + if (sfpt->find_edge(referent) == -1) { + sfpt->add_req(referent); + igvn()._worklist.push(sfpt); + } + } + // Eliminate processed RFs. They become redundant once reachability edges are added. + bool progress = (for_removal.size() > 0); + while (for_removal.size() > 0) { + remove_rf(for_removal.pop()->as_ReachabilityFence()); + } + + assert(C->reachability_fences_count() == no_of_constant_rfs, ""); + return progress; +} + +//====================================================================== +//---------------------------- Phase 3 --------------------------------- + +// Find a point in CFG right after safepoint node to insert reachability fence. +static Node* sfpt_ctrl_out(SafePointNode* sfpt) { + if (sfpt->is_Call()) { + CallProjections callprojs; + sfpt->as_Call()->extract_projections(&callprojs, + false /*separate_io_proj*/, + false /*do_asserts*/, + true /*allow_handlers*/); + // Calls can have multiple control projections. However, reachability edge expansion + // happens during final graph reshaping which is performed very late in compilation pipeline. + // The assumption here is that the control path chosen for insertion can't go away. + // So, materializing a reachability fence on a single control path produced by a call + // is enough to keep the referent oop alive across the call. + if (callprojs.fallthrough_catchproj != nullptr) { + return callprojs.fallthrough_catchproj; + } else if (callprojs.catchall_catchproj != nullptr) { + return callprojs.catchall_catchproj; // rethrow stub + } else if (callprojs.fallthrough_proj != nullptr) { + return callprojs.fallthrough_proj; // no exceptions thrown + } else { + ShouldNotReachHere(); + } + } else { + return sfpt; + } +} + +// Phase 3: materialize reachability fences from reachability edges on safepoints. +// Turn extra safepoint edges into reachability fences immediately following the safepoint. +// +// NB! As of now, a special care is needed to properly enumerate reachability edges because +// there are other use cases for non-debug safepoint edges. expand_reachability_edges() runs +// after macro expansion where runtime calls during array allocation are annotated with +// valid_length_test_input as an extra edges. Until there's a mechanism to distinguish between +// different types of non-debug edges, unrelated cases are filtered out explicitly and in ad-hoc manner. +void Compile::expand_reachability_edges(Unique_Node_List& safepoints) { + for (uint i = 0; i < safepoints.size(); i++) { + SafePointNode* sfpt = safepoints.at(i)->as_SafePoint(); + + uint rf_offset = rf_base_offset(sfpt); + if (sfpt->jvms() != nullptr && sfpt->req() > rf_offset) { + assert(is_interfering_sfpt_candidate(sfpt), ""); + Node* ctrl_out = sfpt_ctrl_out(sfpt); + Node* ctrl_end = ctrl_out->unique_ctrl_out(); + + Node* extra_edge = nullptr; + if (sfpt->is_Call()) { + address entry = sfpt->as_Call()->entry_point(); + if (entry == OptoRuntime::new_array_Java() || + entry == OptoRuntime::new_array_nozero_Java()) { + // valid_length_test_input is appended during macro expansion at the very end + int last_idx = sfpt->req() - 1; + extra_edge = sfpt->in(last_idx); + sfpt->del_req(last_idx); + } + } + + while (sfpt->req() > rf_offset) { + int idx = sfpt->req() - 1; + Node* referent = sfpt->in(idx); + sfpt->del_req(idx); + + Node* new_rf = new ReachabilityFenceNode(C, ctrl_out, referent); + ctrl_end->replace_edge(ctrl_out, new_rf); + ctrl_end = new_rf; + } + + if (extra_edge != nullptr) { + sfpt->add_req(extra_edge); // Add valid_length_test_input edge back + } + } + } +} diff --git a/src/hotspot/share/opto/reachability.hpp b/src/hotspot/share/opto/reachability.hpp new file mode 100644 index 00000000000..ba435c8484f --- /dev/null +++ b/src/hotspot/share/opto/reachability.hpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2026, 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. + * + */ +#ifndef SHARE_OPTO_REACHABILITY_HPP +#define SHARE_OPTO_REACHABILITY_HPP + +#include "opto/multnode.hpp" +#include "opto/node.hpp" +#include "opto/opcodes.hpp" +#include "opto/type.hpp" + +//------------------------ReachabilityFenceNode-------------------------- +// Represents a Reachability Fence (RF) in the code. +// +// RF ensures that the given object (referent) remains strongly reachable regardless of +// any optimizing transformations the virtual machine may perform that might otherwise +// allow the object to become unreachable. + +// java.lang.ref.Reference::reachabilityFence calls are intrinsified into ReachabilityFence nodes. +// +// More details in reachability.cpp. +class ReachabilityFenceNode : public Node { +public: + ReachabilityFenceNode(Compile* C, Node* ctrl, Node* referent) + : Node(1) { + assert(referent->bottom_type()->isa_oopptr() || + referent->bottom_type()->isa_narrowoop() != nullptr || + referent->bottom_type() == TypePtr::NULL_PTR, + "%s", Type::str(referent->bottom_type())); + init_class_id(Class_ReachabilityFence); + init_req(TypeFunc::Control, ctrl); + add_req(referent); + C->add_reachability_fence(this); + } + virtual int Opcode() const; + virtual bool is_CFG() const { return true; } + virtual uint hash() const { return NO_HASH; } // CFG nodes do not hash + virtual bool depends_only_on_test() const { return false; }; + virtual uint ideal_reg() const { return 0; } // not matched in the AD file + virtual const Type* bottom_type() const { return Type::CONTROL; } + virtual const RegMask& in_RegMask(uint idx) const { + // Fake input register mask for the referent: accepts all registers and all stack slots. + // This avoids redundant register moves around reachability fences. + return RegMask::ALL; + } + virtual const RegMask& out_RegMask() const { + return RegMask::EMPTY; + } + + virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); + virtual Node* Identity(PhaseGVN* phase); + + Node* referent() const { return in(1); } + bool is_redundant(PhaseGVN& gvn); + bool clear_referent(PhaseIterGVN& phase); + +#ifndef PRODUCT + virtual void format(PhaseRegAlloc* ra, outputStream* st) const; + virtual void emit(C2_MacroAssembler* masm, PhaseRegAlloc* ra) const; +#endif +}; + +#endif // SHARE_OPTO_REACHABILITY_HPP diff --git a/src/hotspot/share/opto/regmask.hpp b/src/hotspot/share/opto/regmask.hpp index 421031fdf61..99b10c1c557 100644 --- a/src/hotspot/share/opto/regmask.hpp +++ b/src/hotspot/share/opto/regmask.hpp @@ -285,8 +285,9 @@ class RegMask { _rm_word_ext = NEW_ARENA_ARRAY(_arena, uintptr_t, new_ext_size); } else { assert(_original_ext_address == &_rm_word_ext, "clone sanity check"); - _rm_word_ext = REALLOC_ARENA_ARRAY(_arena, uintptr_t, _rm_word_ext, + _rm_word_ext = REALLOC_ARENA_ARRAY(_arena, _rm_word_ext, old_ext_size, new_ext_size); + } if (initialize_by_infinite_stack) { int fill = 0; diff --git a/src/hotspot/share/opto/runtime.cpp b/src/hotspot/share/opto/runtime.cpp index 0d2dbb813bd..c01e8578e43 100644 --- a/src/hotspot/share/opto/runtime.cpp +++ b/src/hotspot/share/opto/runtime.cpp @@ -24,6 +24,7 @@ #include "classfile/vmClasses.hpp" #include "classfile/vmSymbols.hpp" +#include "code/aotCodeCache.hpp" #include "code/codeCache.hpp" #include "code/compiledIC.hpp" #include "code/nmethod.hpp" @@ -154,7 +155,8 @@ static bool check_compiled_frame(JavaThread* thread) { bool OptoRuntime::generate(ciEnv* env) { C2_STUBS_DO(GEN_C2_BLOB, GEN_C2_STUB) - + // disallow any further c2 stub generation + AOTCodeCache::set_c2_stubs_complete(); return true; } diff --git a/src/hotspot/share/opto/split_if.cpp b/src/hotspot/share/opto/split_if.cpp index 91595c57a10..e5f8043ae19 100644 --- a/src/hotspot/share/opto/split_if.cpp +++ b/src/hotspot/share/opto/split_if.cpp @@ -85,7 +85,7 @@ bool PhaseIdealLoop::split_up( Node *n, Node *blk1, Node *blk2 ) { if( split_up( n->in(i), blk1, blk2 ) ) { // Got split recursively and self went dead? if (n->outcnt() == 0) - _igvn.remove_dead_node(n); + _igvn.remove_dead_node(n, PhaseIterGVN::NodeOrigin::Graph); return true; } } @@ -273,7 +273,7 @@ void PhaseIdealLoop::clone_loadklass_nodes_at_cmp_index(const Node* n, Node* cmp _igvn.replace_input_of(decode_clone, 1, loadklass_clone); _igvn.replace_input_of(loadklass_clone, MemNode::Address, addp_clone); if (decode->outcnt() == 0) { - _igvn.remove_dead_node(decode); + _igvn.remove_dead_node(decode, PhaseIterGVN::NodeOrigin::Graph); } } } @@ -290,7 +290,7 @@ void PhaseIdealLoop::clone_loadklass_nodes_at_cmp_index(const Node* n, Node* cmp _igvn.replace_input_of(cmp, i, loadklass_clone); _igvn.replace_input_of(loadklass_clone, MemNode::Address, addp_clone); if (loadklass->outcnt() == 0) { - _igvn.remove_dead_node(loadklass); + _igvn.remove_dead_node(loadklass, PhaseIterGVN::NodeOrigin::Graph); } } } @@ -369,7 +369,7 @@ bool PhaseIdealLoop::clone_cmp_down(Node* n, const Node* blk1, const Node* blk2) _igvn.replace_input_of(x2, 1, x1); _igvn.replace_input_of(iff, 1, x2); } - _igvn.remove_dead_node(u); + _igvn.remove_dead_node(u, PhaseIterGVN::NodeOrigin::Graph); --j; } else { // We might see an Opaque1 from a loop limit check here @@ -385,7 +385,7 @@ bool PhaseIdealLoop::clone_cmp_down(Node* n, const Node* blk1, const Node* blk2) --j; } } - _igvn.remove_dead_node(bol); + _igvn.remove_dead_node(bol, PhaseIterGVN::NodeOrigin::Graph); --i; } } @@ -403,7 +403,7 @@ bool PhaseIdealLoop::clone_cmp_down(Node* n, const Node* blk1, const Node* blk2) register_new_node(x, ctrl_or_self(use)); _igvn.replace_input_of(use, pos, x); } - _igvn.remove_dead_node(n); + _igvn.remove_dead_node(n, PhaseIterGVN::NodeOrigin::Graph); return true; } @@ -517,7 +517,7 @@ Node *PhaseIdealLoop::spinup( Node *iff_dom, Node *new_false, Node *new_true, No Node *t = _igvn.hash_find_insert(phi_post); if( t ) { // See if we already have this one // phi_post will not be used, so kill it - _igvn.remove_dead_node(phi_post); + _igvn.remove_dead_node(phi_post, PhaseIterGVN::NodeOrigin::Speculative); phi_post->destruct(&_igvn); phi_post = t; } else { @@ -647,7 +647,7 @@ void PhaseIdealLoop::do_split_if(Node* iff, RegionNode** new_false_region, Regio Node* m = n->out(j); // If m is dead, throw it away, and declare progress if (_loop_or_ctrl[m->_idx] == nullptr) { - _igvn.remove_dead_node(m); + _igvn.remove_dead_node(m, PhaseIterGVN::NodeOrigin::Graph); // fall through } else if (m != iff && split_up(m, region, iff)) { @@ -704,7 +704,10 @@ void PhaseIdealLoop::do_split_if(Node* iff, RegionNode** new_false_region, Regio new_true = ifpx; } } - _igvn.remove_dead_node(new_iff); + assert(new_false != nullptr, "iff is malformed"); + assert(new_true != nullptr, "iff is malformed"); + + _igvn.remove_dead_node(new_iff, PhaseIterGVN::NodeOrigin::Speculative); // Lazy replace IDOM info with the region's dominator replace_node_and_forward_ctrl(iff, region_dom); // Break the self-cycle. Required for forward_ctrl to work on region. @@ -720,7 +723,7 @@ void PhaseIdealLoop::do_split_if(Node* iff, RegionNode** new_false_region, Regio for (DUIterator k = region->outs(); region->has_out(k); k++) { Node* phi = region->out(k); if (!phi->in(0)) { // Dead phi? Remove it - _igvn.remove_dead_node(phi); + _igvn.remove_dead_node(phi, PhaseIterGVN::NodeOrigin::Graph); } else if (phi == region) { // Found the self-reference continue; // No roll-back of DUIterator } else if (phi->is_Phi()) { // Expected common case: Phi hanging off of Region @@ -739,7 +742,7 @@ void PhaseIdealLoop::do_split_if(Node* iff, RegionNode** new_false_region, Regio handle_use(use, phi, &phi_cache, region_dom, new_false, new_true, old_false, old_true); } // End of while phi has uses // Remove the dead Phi - _igvn.remove_dead_node( phi ); + _igvn.remove_dead_node(phi, PhaseIterGVN::NodeOrigin::Graph); } else { assert(phi->in(0) == region, "Inconsistent graph"); // Random memory op guarded by Region. Compute new DEF for USE. @@ -752,7 +755,7 @@ void PhaseIdealLoop::do_split_if(Node* iff, RegionNode** new_false_region, Regio --k; } // End of while merge point has phis - _igvn.remove_dead_node(region); + _igvn.remove_dead_node(region, PhaseIterGVN::NodeOrigin::Graph); // Control is updated here to a region, which is not a test, so any node that // depends_only_on_test must be pinned diff --git a/src/hotspot/share/opto/subtypenode.cpp b/src/hotspot/share/opto/subtypenode.cpp index 69b7058d053..317b839fbd4 100644 --- a/src/hotspot/share/opto/subtypenode.cpp +++ b/src/hotspot/share/opto/subtypenode.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright Amazon.com Inc. 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 +178,7 @@ bool SubTypeCheckNode::verify(PhaseGVN* phase) { return true; } const Type* cached_t = Value(phase); // cache the type to validate consistency - switch (C->static_subtype_check(superk, subk)) { + switch (C->static_subtype_check(superk, subk, false)) { case Compile::SSC_easy_test: { return verify_helper(phase, load_klass(phase), cached_t); } diff --git a/src/hotspot/share/opto/superword.cpp b/src/hotspot/share/opto/superword.cpp index d878b2b1d3d..53845a94c1c 100644 --- a/src/hotspot/share/opto/superword.cpp +++ b/src/hotspot/share/opto/superword.cpp @@ -2500,7 +2500,9 @@ static bool can_subword_truncate(Node* in, const Type* type) { switch (opc) { case Op_AbsI: case Op_DivI: + case Op_UDivI: case Op_ModI: + case Op_UModI: case Op_MinI: case Op_MaxI: case Op_CMoveI: diff --git a/src/hotspot/share/opto/traceAutoVectorizationTag.hpp b/src/hotspot/share/opto/traceAutoVectorizationTag.hpp index 4f67aff9b07..47def1aed1e 100644 --- a/src/hotspot/share/opto/traceAutoVectorizationTag.hpp +++ b/src/hotspot/share/opto/traceAutoVectorizationTag.hpp @@ -142,7 +142,7 @@ class TraceAutoVectorizationTagValidator { ~TraceAutoVectorizationTagValidator() { if (_bad != nullptr) { - FREE_C_HEAP_ARRAY(char, _bad); + FREE_C_HEAP_ARRAY(_bad); } } diff --git a/src/hotspot/share/opto/traceMergeStoresTag.hpp b/src/hotspot/share/opto/traceMergeStoresTag.hpp index 214173c02f7..32ade413673 100644 --- a/src/hotspot/share/opto/traceMergeStoresTag.hpp +++ b/src/hotspot/share/opto/traceMergeStoresTag.hpp @@ -111,7 +111,7 @@ namespace TraceMergeStores { ~TagValidator() { if (_bad != nullptr) { - FREE_C_HEAP_ARRAY(char, _bad); + FREE_C_HEAP_ARRAY(_bad); } } diff --git a/src/hotspot/share/opto/type.cpp b/src/hotspot/share/opto/type.cpp index aab2ea3cd3b..308ec819773 100644 --- a/src/hotspot/share/opto/type.cpp +++ b/src/hotspot/share/opto/type.cpp @@ -3489,7 +3489,7 @@ TypeOopPtr::TypeOopPtr(TYPES t, PTR ptr, ciKlass* k, const TypeInterfaces* inter #ifdef _LP64 if (_offset > 0 || _offset == Type::OffsetTop || _offset == Type::OffsetBot) { if (_offset == oopDesc::klass_offset_in_bytes()) { - _is_ptr_to_narrowklass = UseCompressedClassPointers; + _is_ptr_to_narrowklass = true; } else if (klass() == nullptr) { // Array with unknown body type assert(this->isa_aryptr(), "only arrays without klass"); diff --git a/src/hotspot/share/opto/vector.cpp b/src/hotspot/share/opto/vector.cpp index f44df7e6da2..d35717c5922 100644 --- a/src/hotspot/share/opto/vector.cpp +++ b/src/hotspot/share/opto/vector.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -455,14 +455,12 @@ void PhaseVector::expand_vunbox_node(VectorUnboxNode* vec_unbox) { gvn.record_for_igvn(local_mem); BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2(); C2OptAccess access(gvn, ctrl, local_mem, decorators, T_OBJECT, obj, addr); - const Type* type = TypeOopPtr::make_from_klass(field->type()->as_klass()); - vec_field_ld = bs->load_at(access, type); - } - // For proper aliasing, attach concrete payload type. - ciKlass* payload_klass = ciTypeArrayKlass::make(bt); - const Type* payload_type = TypeAryPtr::make_from_klass(payload_klass)->cast_to_ptr_type(TypePtr::NotNull); - vec_field_ld = gvn.transform(new CastPPNode(nullptr, vec_field_ld, payload_type)); + // For proper aliasing, attach concrete payload type. + ciKlass* payload_klass = ciTypeArrayKlass::make(bt); + const Type* payload_type = TypeAryPtr::make_from_klass(payload_klass)->cast_to_ptr_type(TypePtr::NotNull); + vec_field_ld = bs->load_at(access, payload_type); + } Node* adr = kit.array_element_address(vec_field_ld, gvn.intcon(0), bt); const TypePtr* adr_type = adr->bottom_type()->is_ptr(); diff --git a/src/hotspot/share/opto/vectornode.cpp b/src/hotspot/share/opto/vectornode.cpp index 6012bdef86e..d19aa476196 100644 --- a/src/hotspot/share/opto/vectornode.cpp +++ b/src/hotspot/share/opto/vectornode.cpp @@ -1047,6 +1047,20 @@ Node* VectorNode::Ideal(PhaseGVN* phase, bool can_reshape) { return nullptr; } +// Traverses a chain of VectorMaskCast and returns the first non VectorMaskCast node. +// +// Due to the unique nature of vector masks, for specific IR patterns, +// VectorMaskCast does not affect the output results. For example: +// (VectorStoreMask (VectorMaskCast* (VectorLoadMask x))) => (x) +// x remains to be a bool vector with no changes. +// This function can be used to eliminate the VectorMaskCast in such patterns. +Node* VectorNode::uncast_mask(Node* n) { + while (n->Opcode() == Op_VectorMaskCast) { + n = n->in(1); + } + return n; +} + // Return initial Pack node. Additional operands added with add_opd() calls. PackNode* PackNode::make(Node* s, uint vlen, BasicType bt) { const TypeVect* vt = TypeVect::make(bt, vlen); @@ -1246,6 +1260,10 @@ int ReductionNode::opcode(int opc, BasicType bt) { assert(bt == T_LONG, "must be"); vopc = Op_AddReductionVL; break; + case Op_AddHF: + assert(bt == T_SHORT, "must be"); + vopc = Op_AddReductionVHF; + break; case Op_AddF: assert(bt == T_FLOAT, "must be"); vopc = Op_AddReductionVF; @@ -1270,6 +1288,10 @@ int ReductionNode::opcode(int opc, BasicType bt) { assert(bt == T_LONG, "must be"); vopc = Op_MulReductionVL; break; + case Op_MulHF: + assert(bt == T_SHORT, "must be"); + vopc = Op_MulReductionVHF; + break; case Op_MulF: assert(bt == T_FLOAT, "must be"); vopc = Op_MulReductionVF; @@ -1418,10 +1440,12 @@ ReductionNode* ReductionNode::make(int opc, Node* ctrl, Node* n1, Node* n2, Basi switch (vopc) { case Op_AddReductionVI: return new AddReductionVINode(ctrl, n1, n2); case Op_AddReductionVL: return new AddReductionVLNode(ctrl, n1, n2); + case Op_AddReductionVHF: return new AddReductionVHFNode(ctrl, n1, n2, requires_strict_order); case Op_AddReductionVF: return new AddReductionVFNode(ctrl, n1, n2, requires_strict_order); case Op_AddReductionVD: return new AddReductionVDNode(ctrl, n1, n2, requires_strict_order); case Op_MulReductionVI: return new MulReductionVINode(ctrl, n1, n2); case Op_MulReductionVL: return new MulReductionVLNode(ctrl, n1, n2); + case Op_MulReductionVHF: return new MulReductionVHFNode(ctrl, n1, n2, requires_strict_order); case Op_MulReductionVF: return new MulReductionVFNode(ctrl, n1, n2, requires_strict_order); case Op_MulReductionVD: return new MulReductionVDNode(ctrl, n1, n2, requires_strict_order); case Op_MinReductionV: return new MinReductionVNode (ctrl, n1, n2); @@ -1495,10 +1519,12 @@ Node* VectorLoadMaskNode::Identity(PhaseGVN* phase) { Node* VectorStoreMaskNode::Identity(PhaseGVN* phase) { // Identity transformation on boolean vectors. - // VectorStoreMask (VectorLoadMask bv) elem_size ==> bv + // VectorStoreMask (VectorMaskCast* VectorLoadMask bv) elem_size ==> bv // vector[n]{bool} => vector[n]{t} => vector[n]{bool} - if (in(1)->Opcode() == Op_VectorLoadMask) { - return in(1)->in(1); + Node* in1 = VectorNode::uncast_mask(in(1)); + if (in1->Opcode() == Op_VectorLoadMask) { + assert(length() == in1->as_Vector()->length(), "vector length must match"); + return in1->in(1); } return this; } @@ -1597,6 +1623,8 @@ Node* ReductionNode::make_identity_con_scalar(PhaseGVN& gvn, int sopc, BasicType return nullptr; } break; + case Op_AddReductionVHF: + return gvn.makecon(TypeH::ZERO); case Op_AddReductionVI: // fallthrough case Op_AddReductionVL: // fallthrough case Op_AddReductionVF: // fallthrough @@ -1608,6 +1636,8 @@ Node* ReductionNode::make_identity_con_scalar(PhaseGVN& gvn, int sopc, BasicType return gvn.makecon(TypeInt::ONE); case Op_MulReductionVL: return gvn.makecon(TypeLong::ONE); + case Op_MulReductionVHF: + return gvn.makecon(TypeH::ONE); case Op_MulReductionVF: return gvn.makecon(TypeF::ONE); case Op_MulReductionVD: @@ -1700,12 +1730,14 @@ bool ReductionNode::auto_vectorization_requires_strict_order(int vopc) { // These are cases that all have associative operations, which can // thus be reordered, allowing non-strict order reductions. return false; + case Op_AddReductionVHF: + case Op_MulReductionVHF: 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 + // so AddReductionVHF/VF/VD and MulReductionVHF/VF/VD require strict ordering // in auto-vectorization. return true; default: @@ -1959,11 +1991,12 @@ Node* VectorMaskOpNode::Ideal(PhaseGVN* phase, bool can_reshape) { } Node* VectorMaskCastNode::Identity(PhaseGVN* phase) { - Node* in1 = in(1); - // VectorMaskCast (VectorMaskCast x) => x - if (in1->Opcode() == Op_VectorMaskCast && - vect_type()->eq(in1->in(1)->bottom_type())) { - return in1->in(1); + // (VectorMaskCast+ x) => (x) + // If the types of the input and output nodes in a VectorMaskCast chain are + // exactly the same, the intermediate VectorMaskCast nodes can be eliminated. + Node* n = VectorNode::uncast_mask(this); + if (vect_type()->eq(n->bottom_type())) { + return n; } return this; } @@ -2432,67 +2465,68 @@ bool MulVLNode::has_uint_inputs() const { has_vector_elements_fit_uint(in(2)); } -static Node* UMinMaxV_Ideal(Node* n, PhaseGVN* phase, bool can_reshape) { +static Node* MinMaxV_Common_Ideal(MinMaxVNode* n, PhaseGVN* phase, bool can_reshape) { int vopc = n->Opcode(); - assert(vopc == Op_UMinV || vopc == Op_UMaxV, "Unexpected opcode"); + int min_opcode = n->min_opcode(); + int max_opcode = n->max_opcode(); - Node* umin = nullptr; - Node* umax = nullptr; + Node* min_op = nullptr; + Node* max_op = nullptr; int lopc = n->in(1)->Opcode(); int ropc = n->in(2)->Opcode(); - if (lopc == Op_UMinV && ropc == Op_UMaxV) { - umin = n->in(1); - umax = n->in(2); - } else if (lopc == Op_UMaxV && ropc == Op_UMinV) { - umin = n->in(2); - umax = n->in(1); + if (lopc == min_opcode && ropc == max_opcode) { + min_op = n->in(1); + max_op = n->in(2); + } else if (lopc == max_opcode && ropc == min_opcode) { + min_op = n->in(2); + max_op = n->in(1); } else { return nullptr; } - // UMin (UMin(a, b), UMax(a, b)) => UMin(a, b) - // UMin (UMax(a, b), UMin(b, a)) => UMin(a, b) - // UMax (UMin(a, b), UMax(a, b)) => UMax(a, b) - // UMax (UMax(a, b), UMin(b, a)) => UMax(a, b) - if (umin != nullptr && umax != nullptr) { - if ((umin->in(1) == umax->in(1) && umin->in(2) == umax->in(2)) || - (umin->in(2) == umax->in(1) && umin->in(1) == umax->in(2))) { - if (vopc == Op_UMinV) { - return new UMinVNode(umax->in(1), umax->in(2), n->bottom_type()->is_vect()); - } else { - return new UMaxVNode(umax->in(1), umax->in(2), n->bottom_type()->is_vect()); + // Min (Min(a, b), Max(a, b)) => Min(a, b) + // Min (Max(a, b), Min(b, a)) => Min(a, b) + // Max (Min(a, b), Max(a, b)) => Max(a, b) + // Max (Max(a, b), Min(b, a)) => Max(a, b) + + if (min_op != nullptr && max_op != nullptr) { + // Skip if predication status is inconsistent across n, min_op, and max_op, + // or if predicated operands carry different masks. + if (n->is_predicated_vector() != min_op->is_predicated_vector() || + min_op->is_predicated_vector() != max_op->is_predicated_vector()) { + return nullptr; + } + if (min_op->is_predicated_vector() && + !(n->in(3) == min_op->in(3) && min_op->in(3) == max_op->in(3))) { + return nullptr; + } + + if ((min_op->in(1) == max_op->in(1) && min_op->in(2) == max_op->in(2)) || + (min_op->in(2) == max_op->in(1) && min_op->in(1) == max_op->in(2))) { + // Use n->in(1) inputs for the result to preserve correct merge-masking + // passthrough: inactive lanes use in(1), so result->in(1) must equal + // n->in(1)->in(1) to maintain the original passthrough semantics. + VectorNode* result = VectorNode::make(vopc, n->in(1)->in(1), n->in(1)->in(2), n->bottom_type()->is_vect()); + if (n->is_predicated_vector()) { + result->add_req(n->in(3)); + result->add_flag(Node::Flag_is_predicated_vector); } + return result; } } return nullptr; } -Node* UMinVNode::Ideal(PhaseGVN* phase, bool can_reshape) { - Node* progress = UMinMaxV_Ideal(this, phase, can_reshape); +Node* MinMaxVNode::Ideal(PhaseGVN* phase, bool can_reshape) { + Node* progress = MinMaxV_Common_Ideal(this, phase, can_reshape); if (progress != nullptr) return progress; return VectorNode::Ideal(phase, can_reshape); } -Node* UMinVNode::Identity(PhaseGVN* phase) { - // UMin (a, a) => a - if (in(1) == in(2)) { - return in(1); - } - return this; -} - -Node* UMaxVNode::Ideal(PhaseGVN* phase, bool can_reshape) { - Node* progress = UMinMaxV_Ideal(this, phase, can_reshape); - if (progress != nullptr) return progress; - - return VectorNode::Ideal(phase, can_reshape); -} - -Node* UMaxVNode::Identity(PhaseGVN* phase) { - // UMax (a, a) => a +Node* MinMaxVNode::Identity(PhaseGVN* phase) { if (in(1) == in(2)) { return in(1); } @@ -2502,4 +2536,5 @@ Node* UMaxVNode::Identity(PhaseGVN* phase) { void VectorBoxAllocateNode::dump_spec(outputStream *st) const { CallStaticJavaNode::dump_spec(st); } + #endif // !PRODUCT diff --git a/src/hotspot/share/opto/vectornode.hpp b/src/hotspot/share/opto/vectornode.hpp index dce43f92905..91cff9fcae8 100644 --- a/src/hotspot/share/opto/vectornode.hpp +++ b/src/hotspot/share/opto/vectornode.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright 2026 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 @@ -195,6 +196,7 @@ class VectorNode : public TypeNode { static bool is_scalar_op_that_returns_int_but_vector_op_returns_long(int opc); static bool is_reinterpret_opcode(int opc); + static Node* uncast_mask(Node* n); static void trace_new_vector(Node* n, const char* context) { #ifdef ASSERT @@ -321,7 +323,7 @@ class ReductionNode : public Node { virtual uint size_of() const { return sizeof(*this); } // Floating-point addition and multiplication are non-associative, so - // AddReductionVF/D and MulReductionVF/D require strict ordering + // AddReductionVHF/F/D and MulReductionVHF/F/D require strict ordering // in auto-vectorization. Vector API can generate AddReductionVF/D // and MulReductionVF/VD without strict ordering, which can benefit // some platforms. @@ -358,6 +360,35 @@ public: virtual int Opcode() const; }; +// Vector add half float as a reduction +class AddReductionVHFNode : public ReductionNode { +private: + // True if add reduction operation for half floats requires strict ordering. + // As an example - The value is true when add reduction for half floats is auto-vectorized + // as auto-vectorization mandates strict ordering but the value is false when this node + // is generated through VectorAPI as VectorAPI does not impose any such rules on ordering. + const bool _requires_strict_order; + +public: + // _requires_strict_order is set to true by default as mandated by auto-vectorization + AddReductionVHFNode(Node* ctrl, Node* in1, Node* in2, bool requires_strict_order = true) : + ReductionNode(ctrl, in1, in2), _requires_strict_order(requires_strict_order) {} + + int Opcode() const override; + bool requires_strict_order() const override { return _requires_strict_order; } + + uint hash() const override { return Node::hash() + _requires_strict_order; } + + bool cmp(const Node& n) const override { + return Node::cmp(n) && _requires_strict_order == ((ReductionNode&)n).requires_strict_order(); + } + + uint size_of() const override { return sizeof(*this); } + + const Type* bottom_type() const override { return Type::HALF_FLOAT; } + uint ideal_reg() const override { return Op_RegF; } +}; + // Vector add float as a reduction class AddReductionVFNode : public ReductionNode { private: @@ -367,7 +398,7 @@ private: // is generated through VectorAPI as VectorAPI does not impose any such rules on ordering. const bool _requires_strict_order; public: - //_requires_strict_order is set to true by default as mandated by auto-vectorization + // _requires_strict_order is set to true by default as mandated by auto-vectorization AddReductionVFNode(Node* ctrl, Node* in1, Node* in2, bool requires_strict_order = true) : ReductionNode(ctrl, in1, in2), _requires_strict_order(requires_strict_order) {} @@ -393,7 +424,7 @@ private: // is generated through VectorAPI as VectorAPI does not impose any such rules on ordering. const bool _requires_strict_order; public: - //_requires_strict_order is set to true by default as mandated by auto-vectorization + // _requires_strict_order is set to true by default as mandated by auto-vectorization AddReductionVDNode(Node* ctrl, Node* in1, Node* in2, bool requires_strict_order = true) : ReductionNode(ctrl, in1, in2), _requires_strict_order(requires_strict_order) {} @@ -577,6 +608,35 @@ public: virtual int Opcode() const; }; +// Vector multiply half float as a reduction +class MulReductionVHFNode : public ReductionNode { +private: + // True if mul reduction operation for half floats requires strict ordering. + // As an example - The value is true when mul reduction for half floats is auto-vectorized + // as auto-vectorization mandates strict ordering but the value is false when this node + // is generated through VectorAPI as VectorAPI does not impose any such rules on ordering. + const bool _requires_strict_order; + +public: + // _requires_strict_order is set to true by default as mandated by auto-vectorization + MulReductionVHFNode(Node* ctrl, Node* in1, Node* in2, bool requires_strict_order = true) : + ReductionNode(ctrl, in1, in2), _requires_strict_order(requires_strict_order) {} + + int Opcode() const override; + bool requires_strict_order() const override { return _requires_strict_order; } + + uint hash() const override { return Node::hash() + _requires_strict_order; } + + bool cmp(const Node& n) const override { + return Node::cmp(n) && _requires_strict_order == ((ReductionNode&)n).requires_strict_order(); + } + + uint size_of() const override { return sizeof(*this); } + + const Type* bottom_type() const override { return Type::HALF_FLOAT; } + uint ideal_reg() const override { return Op_RegF; } +}; + // Vector multiply float as a reduction class MulReductionVFNode : public ReductionNode { // True if mul reduction operation for floats requires strict ordering. @@ -585,7 +645,7 @@ class MulReductionVFNode : public ReductionNode { // is generated through VectorAPI as VectorAPI does not impose any such rules on ordering. const bool _requires_strict_order; public: - //_requires_strict_order is set to true by default as mandated by auto-vectorization + // _requires_strict_order is set to true by default as mandated by auto-vectorization MulReductionVFNode(Node* ctrl, Node* in1, Node* in2, bool requires_strict_order = true) : ReductionNode(ctrl, in1, in2), _requires_strict_order(requires_strict_order) {} @@ -610,7 +670,7 @@ class MulReductionVDNode : public ReductionNode { // is generated through VectorAPI as VectorAPI does not impose any such rules on ordering. const bool _requires_strict_order; public: - //_requires_strict_order is set to true by default as mandated by auto-vectorization + // _requires_strict_order is set to true by default as mandated by auto-vectorization MulReductionVDNode(Node* ctrl, Node* in1, Node* in2, bool requires_strict_order = true) : ReductionNode(ctrl, in1, in2), _requires_strict_order(requires_strict_order) {} @@ -662,10 +722,22 @@ public: virtual int Opcode() const; }; -// Vector Min -class MinVNode : public VectorNode { +// Common superclass for Min/Max vector nodes +class MinMaxVNode : public VectorNode { public: - MinVNode(Node* in1, Node* in2, const TypeVect* vt) : VectorNode(in1, in2, vt) {} + MinMaxVNode(Node* in1, Node* in2, const TypeVect* vt) : VectorNode(in1, in2, vt) {} + virtual int min_opcode() const = 0; + virtual int max_opcode() const = 0; + virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); + virtual Node* Identity(PhaseGVN* phase); +}; + +// Vector Min +class MinVNode : public MinMaxVNode { +public: + MinVNode(Node* in1, Node* in2, const TypeVect* vt) : MinMaxVNode(in1, in2, vt) {} + virtual int min_opcode() const { return Op_MinV; } + virtual int max_opcode() const { return Op_MaxV; } virtual int Opcode() const; }; @@ -684,31 +756,33 @@ public: }; // Vector Unsigned Min -class UMinVNode : public VectorNode { +class UMinVNode : public MinMaxVNode { public: - UMinVNode(Node* in1, Node* in2, const TypeVect* vt) : VectorNode(in1, in2 ,vt) { + UMinVNode(Node* in1, Node* in2, const TypeVect* vt) : MinMaxVNode(in1, in2, vt) { assert(is_integral_type(vt->element_basic_type()), ""); } - virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); - virtual Node* Identity(PhaseGVN* phase); + virtual int min_opcode() const { return Op_UMinV; } + virtual int max_opcode() const { return Op_UMaxV; } virtual int Opcode() const; }; // Vector Max -class MaxVNode : public VectorNode { +class MaxVNode : public MinMaxVNode { public: - MaxVNode(Node* in1, Node* in2, const TypeVect* vt) : VectorNode(in1, in2, vt) {} + MaxVNode(Node* in1, Node* in2, const TypeVect* vt) : MinMaxVNode(in1, in2, vt) {} + virtual int min_opcode() const { return Op_MinV; } + virtual int max_opcode() const { return Op_MaxV; } virtual int Opcode() const; }; // Vector Unsigned Max -class UMaxVNode : public VectorNode { +class UMaxVNode : public MinMaxVNode { public: - UMaxVNode(Node* in1, Node* in2, const TypeVect* vt) : VectorNode(in1, in2, vt) { + UMaxVNode(Node* in1, Node* in2, const TypeVect* vt) : MinMaxVNode(in1, in2, vt) { assert(is_integral_type(vt->element_basic_type()), ""); } - virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); - virtual Node* Identity(PhaseGVN* phase); + virtual int min_opcode() const { return Op_UMinV; } + virtual int max_opcode() const { return Op_UMaxV; } virtual int Opcode() const; }; diff --git a/src/hotspot/share/prims/downcallLinker.hpp b/src/hotspot/share/prims/downcallLinker.hpp index 2c2cf053033..519e84281ce 100644 --- a/src/hotspot/share/prims/downcallLinker.hpp +++ b/src/hotspot/share/prims/downcallLinker.hpp @@ -72,7 +72,7 @@ public: bool needs_return_buffer, int captured_state_mask, bool needs_transition) - : StubCodeGenerator(buffer, PrintMethodHandleStubs), + : StubCodeGenerator(buffer, PrintMethodHandleStubs), _signature(signature), _num_args(num_args), _ret_bt(ret_bt), diff --git a/src/hotspot/share/prims/jni.cpp b/src/hotspot/share/prims/jni.cpp index 85207fddf29..d6a435d34d1 100644 --- a/src/hotspot/share/prims/jni.cpp +++ b/src/hotspot/share/prims/jni.cpp @@ -867,8 +867,7 @@ static void jni_invoke_static(JNIEnv *env, JavaValue* result, jobject receiver, // Create object to hold arguments for the JavaCall, and associate it with // the jni parser ResourceMark rm(THREAD); - int number_of_parameters = method->size_of_parameters(); - JavaCallArguments java_args(number_of_parameters); + JavaCallArguments java_args(method->size_of_parameters()); assert(method->is_static(), "method should be static"); @@ -2896,7 +2895,7 @@ JNI_ENTRY(void, jni_ReleaseStringCritical(JNIEnv *env, jstring str, const jchar if (is_latin1) { // For latin1 string, free jchar array allocated by earlier call to GetStringCritical. // This assumes that ReleaseStringCritical bookends GetStringCritical. - FREE_C_HEAP_ARRAY(jchar, chars); + FREE_C_HEAP_ARRAY(chars); } else { // StringDedup can have replaced the value array, so don't fetch the array from 's'. // Instead, we calculate the address based on the jchar array exposed with GetStringCritical. diff --git a/src/hotspot/share/prims/jvmtiAgent.cpp b/src/hotspot/share/prims/jvmtiAgent.cpp index 66cb68b44b0..5fe5378995b 100644 --- a/src/hotspot/share/prims/jvmtiAgent.cpp +++ b/src/hotspot/share/prims/jvmtiAgent.cpp @@ -247,7 +247,7 @@ static void vm_exit(const JvmtiAgent* agent, const char* sub_msg1, const char* s jio_snprintf(buf, len, "%s%s%s%s", not_found_error_msg, agent->name(), sub_msg1, &ebuf[0]); } vm_exit_during_initialization(buf, nullptr); - FREE_C_HEAP_ARRAY(char, buf); + FREE_C_HEAP_ARRAY(buf); } #ifdef ASSERT diff --git a/src/hotspot/share/prims/jvmtiClassFileReconstituter.cpp b/src/hotspot/share/prims/jvmtiClassFileReconstituter.cpp index 5077a1743b9..3037ffa5063 100644 --- a/src/hotspot/share/prims/jvmtiClassFileReconstituter.cpp +++ b/src/hotspot/share/prims/jvmtiClassFileReconstituter.cpp @@ -955,7 +955,7 @@ address JvmtiClassFileReconstituter::writeable_address(size_t size) { * initial_buffer_size; // VM goes belly-up if the memory isn't available, so cannot do OOM processing - _buffer = REALLOC_RESOURCE_ARRAY(u1, _buffer, _buffer_size, new_buffer_size); + _buffer = REALLOC_RESOURCE_ARRAY(_buffer, _buffer_size, new_buffer_size); _buffer_size = new_buffer_size; _buffer_ptr = _buffer + used_size; } diff --git a/src/hotspot/share/prims/jvmtiEventController.cpp b/src/hotspot/share/prims/jvmtiEventController.cpp index cb44b833c48..832c8a33c88 100644 --- a/src/hotspot/share/prims/jvmtiEventController.cpp +++ b/src/hotspot/share/prims/jvmtiEventController.cpp @@ -544,6 +544,11 @@ JvmtiEventControllerPrivate::recompute_env_thread_enabled(JvmtiEnvThreadState* e } switch (JvmtiEnv::get_phase()) { + case JVMTI_PHASE_ONLOAD: + case JVMTI_PHASE_PRIMORDIAL: + case JVMTI_PHASE_START: + now_enabled &= EARLY_EVENT_BITS; + break; case JVMTI_PHASE_DEAD: // no events allowed when dead now_enabled = 0; diff --git a/src/hotspot/share/prims/jvmtiExport.cpp b/src/hotspot/share/prims/jvmtiExport.cpp index fca348fda26..98d8cfd990d 100644 --- a/src/hotspot/share/prims/jvmtiExport.cpp +++ b/src/hotspot/share/prims/jvmtiExport.cpp @@ -1160,7 +1160,7 @@ class JvmtiCompiledMethodLoadEventMark : public JvmtiMethodEventMark { JvmtiCodeBlobEvents::build_jvmti_addr_location_map(nm, &_map, &_map_length); } ~JvmtiCompiledMethodLoadEventMark() { - FREE_C_HEAP_ARRAY(jvmtiAddrLocationMap, _map); + FREE_C_HEAP_ARRAY(_map); } jint code_size() { return _code_size; } diff --git a/src/hotspot/share/prims/jvmtiRedefineClasses.cpp b/src/hotspot/share/prims/jvmtiRedefineClasses.cpp index 13b239b4df0..8beddc5d406 100644 --- a/src/hotspot/share/prims/jvmtiRedefineClasses.cpp +++ b/src/hotspot/share/prims/jvmtiRedefineClasses.cpp @@ -53,6 +53,7 @@ #include "oops/method.hpp" #include "oops/oop.inline.hpp" #include "oops/recordComponent.hpp" +#include "oops/trainingData.hpp" #include "prims/jvmtiImpl.hpp" #include "prims/jvmtiRedefineClasses.hpp" #include "prims/jvmtiThreadState.inline.hpp" @@ -274,6 +275,10 @@ void VM_RedefineClasses::doit() { redefine_single_class(current, _class_defs[i].klass, _scratch_classes[i]); } +#if INCLUDE_CDS + TrainingData::cleanup_after_redefinition(); +#endif + // Flush all compiled code that depends on the classes redefined. flush_dependent_code(); @@ -1481,6 +1486,8 @@ jvmtiError VM_RedefineClasses::load_new_class_versions() { } else { return JVMTI_ERROR_INTERNAL; } + } else if (res != JVMTI_ERROR_NONE) { + return res; } #ifdef ASSERT @@ -2045,7 +2052,7 @@ bool VM_RedefineClasses::rewrite_cp_refs_in_record_attribute(InstanceKlass* scra AnnotationArray* type_annotations = component->type_annotations(); if (type_annotations != nullptr && type_annotations->length() != 0) { int byte_i = 0; // byte index into annotations - if (!rewrite_cp_refs_in_annotations_typeArray(type_annotations, byte_i)) { + if (!rewrite_cp_refs_in_type_annotations_typeArray(type_annotations, byte_i, "record_info")) { log_debug(redefine, class, annotation)("bad record_component_type_annotations at %d", i); // propagate failure back to caller return false; diff --git a/src/hotspot/share/prims/jvmtiTagMap.cpp b/src/hotspot/share/prims/jvmtiTagMap.cpp index 04cb70863cd..26724f42e53 100644 --- a/src/hotspot/share/prims/jvmtiTagMap.cpp +++ b/src/hotspot/share/prims/jvmtiTagMap.cpp @@ -716,7 +716,7 @@ static jint invoke_string_value_callback(jvmtiStringPrimitiveValueCallback cb, user_data); if (is_latin1 && s_len > 0) { - FREE_C_HEAP_ARRAY(jchar, value); + FREE_C_HEAP_ARRAY(value); } return res; } diff --git a/src/hotspot/share/prims/methodHandles.cpp b/src/hotspot/share/prims/methodHandles.cpp index 584f077eddc..03cb98d8e75 100644 --- a/src/hotspot/share/prims/methodHandles.cpp +++ b/src/hotspot/share/prims/methodHandles.cpp @@ -157,6 +157,19 @@ int MethodHandles::ref_kind_to_flags(int ref_kind) { return flags; } +#ifdef ASSERT +const char* MethodHandles::ref_kind_to_verify_msg(int ref_kind) { + switch (ref_kind) { + case JVM_REF_invokeSpecial: return "verify_ref_kind expected invokeSpecial"; + case JVM_REF_invokeStatic: return "verify_ref_kind expected invokeStatic"; + case JVM_REF_invokeVirtual: return "verify_ref_kind expected invokeVirtual"; + case JVM_REF_invokeInterface: return "verify_ref_kind expected invokeInterface"; + default: assert(false, "unexpected ref_kind: %d", ref_kind); + } + return ""; +} +#endif + Handle MethodHandles::resolve_MemberName_type(Handle mname, Klass* caller, TRAPS) { Handle empty; Handle type(THREAD, java_lang_invoke_MemberName::type(mname())); diff --git a/src/hotspot/share/prims/methodHandles.hpp b/src/hotspot/share/prims/methodHandles.hpp index 73da28a6cf5..a2a549fe051 100644 --- a/src/hotspot/share/prims/methodHandles.hpp +++ b/src/hotspot/share/prims/methodHandles.hpp @@ -182,6 +182,8 @@ public: static int ref_kind_to_flags(int ref_kind); + DEBUG_ONLY( static const char* ref_kind_to_verify_msg(int ref_kind); ) + #include CPU_HEADER(methodHandles) // Tracing diff --git a/src/hotspot/share/prims/unsafe.cpp b/src/hotspot/share/prims/unsafe.cpp index c950690e8ab..368c40abbc9 100644 --- a/src/hotspot/share/prims/unsafe.cpp +++ b/src/hotspot/share/prims/unsafe.cpp @@ -702,11 +702,11 @@ static jclass Unsafe_DefineClass_impl(JNIEnv *env, jstring name, jbyteArray data result = JVM_DefineClass(env, utfName, loader, body, length, pd); if (utfName && utfName != buf) { - FREE_C_HEAP_ARRAY(char, utfName); + FREE_C_HEAP_ARRAY(utfName); } free_body: - FREE_C_HEAP_ARRAY(jbyte, body); + FREE_C_HEAP_ARRAY(body); return result; } diff --git a/src/hotspot/share/prims/vectorSupport.cpp b/src/hotspot/share/prims/vectorSupport.cpp index 58f22d38d33..5c6010acdf1 100644 --- a/src/hotspot/share/prims/vectorSupport.cpp +++ b/src/hotspot/share/prims/vectorSupport.cpp @@ -200,7 +200,6 @@ bool VectorSupport::is_unsigned_op(jint id) { } const char* VectorSupport::lanetype2name(LaneType lane_type) { - assert(lane_type >= LT_FLOAT && lane_type <= LT_LONG, ""); const char* lanetype2name[] = { "float", "double", @@ -209,7 +208,11 @@ const char* VectorSupport::lanetype2name(LaneType lane_type) { "int", "long" }; - return lanetype2name[lane_type]; + if (lane_type >= LT_FLOAT && lane_type <= LT_LONG) { + return lanetype2name[lane_type]; + } + assert(false, "unknown lane type: %d", (int)lane_type); + return "illegal"; } int VectorSupport::vop2ideal(jint id, LaneType lt) { @@ -223,7 +226,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_LONG: return Op_AddL; case LT_FLOAT: return Op_AddF; case LT_DOUBLE: return Op_AddD; - default: fatal("ADD: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -235,7 +238,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_LONG: return Op_SubL; case LT_FLOAT: return Op_SubF; case LT_DOUBLE: return Op_SubD; - default: fatal("SUB: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -247,7 +250,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_LONG: return Op_MulL; case LT_FLOAT: return Op_MulF; case LT_DOUBLE: return Op_MulD; - default: fatal("MUL: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -259,7 +262,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_LONG: return Op_DivL; case LT_FLOAT: return Op_DivF; case LT_DOUBLE: return Op_DivD; - default: fatal("DIV: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -271,7 +274,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_LONG: return Op_MinL; case LT_FLOAT: return Op_MinF; case LT_DOUBLE: return Op_MinD; - default: fatal("MIN: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -283,7 +286,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_LONG: return Op_MaxL; case LT_FLOAT: return Op_MaxF; case LT_DOUBLE: return Op_MaxD; - default: fatal("MAX: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -293,7 +296,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: case LT_INT: case LT_LONG: return Op_UMinV; - default: fatal("MIN: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -303,7 +306,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: case LT_INT: case LT_LONG: return Op_UMaxV; - default: fatal("MAX: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -315,7 +318,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_LONG: return Op_AbsL; case LT_FLOAT: return Op_AbsF; case LT_DOUBLE: return Op_AbsD; - default: fatal("ABS: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -327,7 +330,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_LONG: return Op_NegL; case LT_FLOAT: return Op_NegF; case LT_DOUBLE: return Op_NegD; - default: fatal("NEG: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -337,7 +340,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: // fall-through case LT_INT: return Op_AndI; case LT_LONG: return Op_AndL; - default: fatal("AND: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -347,7 +350,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: // fall-through case LT_INT: return Op_OrI; case LT_LONG: return Op_OrL; - default: fatal("OR: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -357,7 +360,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: // fall-through case LT_INT: return Op_XorI; case LT_LONG: return Op_XorL; - default: fatal("XOR: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -365,7 +368,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { switch (lt) { case LT_FLOAT: return Op_SqrtF; case LT_DOUBLE: return Op_SqrtD; - default: fatal("SQRT: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -373,7 +376,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { switch (lt) { case LT_FLOAT: return Op_FmaF; case LT_DOUBLE: return Op_FmaD; - default: fatal("FMA: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -383,7 +386,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: // fall-through case LT_INT: return Op_LShiftI; case LT_LONG: return Op_LShiftL; - default: fatal("LSHIFT: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -393,7 +396,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: // fall-through case LT_INT: return Op_RShiftI; case LT_LONG: return Op_RShiftL; - default: fatal("RSHIFT: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -403,7 +406,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: return Op_URShiftS; case LT_INT: return Op_URShiftI; case LT_LONG: return Op_URShiftL; - default: fatal("URSHIFT: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -413,7 +416,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: // fall-through case LT_INT: // fall-through case LT_LONG: return Op_RotateLeft; - default: fatal("LROTATE: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -423,7 +426,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: // fall-through case LT_INT: // fall-through case LT_LONG: return Op_RotateRight; - default: fatal("RROTATE: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -435,7 +438,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_LONG: // fall-through case LT_FLOAT: // fall-through case LT_DOUBLE: return Op_VectorMaskLastTrue; - default: fatal("MASK_LASTTRUE: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -447,7 +450,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_LONG: // fall-through case LT_FLOAT: // fall-through case LT_DOUBLE: return Op_VectorMaskFirstTrue; - default: fatal("MASK_FIRSTTRUE: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -459,7 +462,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_LONG: // fall-through case LT_FLOAT: // fall-through case LT_DOUBLE: return Op_VectorMaskTrueCount; - default: fatal("MASK_TRUECOUNT: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -471,7 +474,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_LONG: // fall-through case LT_FLOAT: // fall-through case LT_DOUBLE: return Op_VectorMaskToLong; - default: fatal("MASK_TOLONG: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -483,7 +486,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_LONG: // fall-through case LT_FLOAT: // fall-through case LT_DOUBLE: return Op_ExpandV; - default: fatal("EXPAND: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -495,7 +498,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_LONG: // fall-through case LT_FLOAT: // fall-through case LT_DOUBLE: return Op_CompressV; - default: fatal("COMPRESS: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -507,7 +510,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_LONG: // fall-through case LT_FLOAT: // fall-through case LT_DOUBLE: return Op_CompressM; - default: fatal("MASK_COMPRESS: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -517,7 +520,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: // for byte and short types temporarily case LT_INT: return Op_PopCountI; case LT_LONG: return Op_PopCountL; - default: fatal("BILT_COUNT: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -527,7 +530,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: case LT_INT: return Op_CountTrailingZerosI; case LT_LONG: return Op_CountTrailingZerosL; - default: fatal("TZ_COUNT: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -537,7 +540,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: case LT_INT: return Op_CountLeadingZerosI; case LT_LONG: return Op_CountLeadingZerosL; - default: fatal("LZ_COUNT: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -547,7 +550,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: // Op_ReverseI for byte and short case LT_INT: return Op_ReverseI; case LT_LONG: return Op_ReverseL; - default: fatal("REVERSE: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -562,7 +565,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_BYTE: // Intentionally fall-through case LT_INT: return Op_ReverseBytesI; case LT_LONG: return Op_ReverseBytesL; - default: fatal("REVERSE_BYTES: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -573,7 +576,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: // fall-through case LT_INT: // fall-through case LT_LONG: return Op_SaturatingAddV; - default: fatal("S[U]ADD: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -584,7 +587,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: // fall-through case LT_INT: // fall-through case LT_LONG: return Op_SaturatingSubV; - default: fatal("S[U}SUB: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -592,7 +595,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { switch (lt) { case LT_INT: case LT_LONG: return Op_CompressBits; - default: fatal("COMPRESS_BITS: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -600,7 +603,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { switch (lt) { case LT_INT: case LT_LONG: return Op_ExpandBits; - default: fatal("EXPAND_BITS: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -624,7 +627,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case VECTOR_OP_EXPM1: // fall-through case VECTOR_OP_HYPOT: return 0; // not supported; should be handled in Java code - default: fatal("unknown op: %d", vop); + default: return 0; } return 0; // Unimplemented } diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp index 5df1461c0fd..de140fb95ff 100644 --- a/src/hotspot/share/prims/whitebox.cpp +++ b/src/hotspot/share/prims/whitebox.cpp @@ -107,6 +107,7 @@ #if INCLUDE_G1GC #include "gc/g1/g1Arguments.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" +#include "gc/g1/g1CollectorState.inline.hpp" #include "gc/g1/g1ConcurrentMark.hpp" #include "gc/g1/g1ConcurrentMarkThread.inline.hpp" #include "gc/g1/g1HeapRegionManager.hpp" @@ -333,7 +334,6 @@ WB_ENTRY(void, WB_ReadFromNoaccessArea(JNIEnv* env, jobject o)) WB_END WB_ENTRY(void, WB_DecodeNKlassAndAccessKlass(JNIEnv* env, jobject o, jint nKlass)) - assert(UseCompressedClassPointers, "Should only call for UseCompressedClassPointers"); const narrowKlass nk = (narrowKlass)nKlass; const Klass* const k = CompressedKlassPointers::decode_not_null_without_asserts(nKlass); printf("WB_DecodeNKlassAndAccessKlass: nk %u k " PTR_FORMAT "\n", nk, p2i(k)); @@ -1280,8 +1280,8 @@ WB_ENTRY(void, WB_ClearMethodState(JNIEnv* env, jobject o, jobject method)) if (mdo != nullptr) { mdo->init(); ResourceMark rm(THREAD); - int arg_count = mdo->method()->size_of_parameters(); - for (int i = 0; i < arg_count; i++) { + int arg_size = mdo->method()->size_of_parameters(); + for (int i = 0; i < arg_size; i++) { mdo->set_arg_modified(i, 0); } mdo->clean_method_data(/*always_clean*/true); diff --git a/src/hotspot/share/runtime/abstract_vm_version.hpp b/src/hotspot/share/runtime/abstract_vm_version.hpp index 17ade2c068d..794fa4dabcf 100644 --- a/src/hotspot/share/runtime/abstract_vm_version.hpp +++ b/src/hotspot/share/runtime/abstract_vm_version.hpp @@ -45,6 +45,21 @@ class outputStream; class stringStream; enum class vmIntrinsicID; +// Helper macro to test and set VM flag and corresponding cpu feature +#define CHECK_CPU_FEATURE(feature_test_fn, feature) \ + if (feature_test_fn()) { \ + if (FLAG_IS_DEFAULT(Use##feature)) { \ + FLAG_SET_DEFAULT(Use##feature, true); \ + } else if (!Use##feature) { \ + clear_feature(CPU_##feature); \ + } \ + } else if (Use##feature) { \ + if (!FLAG_IS_DEFAULT(Use##feature)) { \ + warning(#feature " instructions are not available on this CPU"); \ + } \ + FLAG_SET_DEFAULT(Use##feature, false); \ + } + // Abstract_VM_Version provides information about the VM. class Abstract_VM_Version: AllStatic { diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index dac34017e45..72e3c4fc4b4 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -533,9 +533,6 @@ static SpecialFlag const special_jvm_flags[] = { { "DynamicDumpSharedSpaces", JDK_Version::jdk(18), JDK_Version::jdk(19), JDK_Version::undefined() }, { "RequireSharedSpaces", JDK_Version::jdk(18), JDK_Version::jdk(19), JDK_Version::undefined() }, { "UseSharedSpaces", JDK_Version::jdk(18), JDK_Version::jdk(19), JDK_Version::undefined() }, -#ifdef _LP64 - { "UseCompressedClassPointers", JDK_Version::jdk(25), JDK_Version::jdk(27), JDK_Version::undefined() }, -#endif { "AggressiveHeap", JDK_Version::jdk(26), JDK_Version::jdk(27), JDK_Version::jdk(28) }, // --- Deprecated alias flags (see also aliased_jvm_flags) - sorted by obsolete_in then expired_in: { "CreateMinidumpOnCrash", JDK_Version::jdk(9), JDK_Version::undefined(), JDK_Version::undefined() }, @@ -546,6 +543,9 @@ static SpecialFlag const special_jvm_flags[] = { #if defined(AARCH64) { "NearCpool", JDK_Version::undefined(), JDK_Version::jdk(25), JDK_Version::undefined() }, #endif +#ifdef _LP64 + { "UseCompressedClassPointers", JDK_Version::jdk(25), JDK_Version::jdk(27), JDK_Version::undefined() }, +#endif { "PSChunkLargeArrays", JDK_Version::jdk(26), JDK_Version::jdk(27), JDK_Version::jdk(28) }, { "ParallelRefProcEnabled", JDK_Version::jdk(26), JDK_Version::jdk(27), JDK_Version::jdk(28) }, @@ -851,7 +851,7 @@ static bool set_string_flag(JVMFlag* flag, const char* value, JVMFlagOrigin orig } if (JVMFlagAccess::set_ccstr(flag, &value, origin) != JVMFlag::SUCCESS) return false; // Contract: JVMFlag always returns a pointer that needs freeing. - FREE_C_HEAP_ARRAY(char, value); + FREE_C_HEAP_ARRAY(value); return true; } @@ -876,9 +876,9 @@ static bool append_to_string_flag(JVMFlag* flag, const char* new_value, JVMFlagO } (void) JVMFlagAccess::set_ccstr(flag, &value, origin); // JVMFlag always returns a pointer that needs freeing. - FREE_C_HEAP_ARRAY(char, value); + FREE_C_HEAP_ARRAY(value); // JVMFlag made its own copy, so I must delete my own temp. buffer. - FREE_C_HEAP_ARRAY(char, free_this_too); + FREE_C_HEAP_ARRAY(free_this_too); return true; } @@ -1013,7 +1013,7 @@ void Arguments::add_string(char*** bldarray, int* count, const char* arg) { if (*bldarray == nullptr) { *bldarray = NEW_C_HEAP_ARRAY(char*, new_count, mtArguments); } else { - *bldarray = REALLOC_C_HEAP_ARRAY(char*, *bldarray, new_count, mtArguments); + *bldarray = REALLOC_C_HEAP_ARRAY(*bldarray, new_count, mtArguments); } (*bldarray)[*count] = os::strdup_check_oom(arg); *count = new_count; @@ -1515,7 +1515,7 @@ void Arguments::set_heap_size() { !FLAG_IS_DEFAULT(MinRAMPercentage) || !FLAG_IS_DEFAULT(InitialRAMPercentage); - const size_t avail_mem = os::physical_memory(); + const physical_memory_size_type avail_mem = os::physical_memory(); // 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 @@ -1554,7 +1554,7 @@ void Arguments::set_heap_size() { } #ifdef _LP64 - if (UseCompressedOops || UseCompressedClassPointers) { + if (UseCompressedOops) { // HeapBaseMinAddress can be greater than default but not less than. if (!FLAG_IS_DEFAULT(HeapBaseMinAddress)) { if (HeapBaseMinAddress < DefaultHeapBaseMinAddress) { @@ -1567,9 +1567,7 @@ void Arguments::set_heap_size() { FLAG_SET_ERGO(HeapBaseMinAddress, DefaultHeapBaseMinAddress); } } - } - if (UseCompressedOops) { uintptr_t heap_end = HeapBaseMinAddress + MaxHeapSize; uintptr_t max_coop_heap = max_heap_for_compressed_oops(); @@ -2052,7 +2050,7 @@ int Arguments::process_patch_mod_option(const char* patch_mod_tail) { *(module_name + module_len) = '\0'; // The path piece begins one past the module_equal sign add_patch_mod_prefix(module_name, module_equal + 1); - FREE_C_HEAP_ARRAY(char, module_name); + FREE_C_HEAP_ARRAY(module_name); if (!create_numbered_module_property("jdk.module.patch", patch_mod_tail, patch_mod_count++)) { return JNI_ENOMEM; } @@ -2203,8 +2201,8 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, JVMFlagOrigin } #endif // !INCLUDE_JVMTI JvmtiAgentList::add_xrun(name, options, false); - FREE_C_HEAP_ARRAY(char, name); - FREE_C_HEAP_ARRAY(char, options); + FREE_C_HEAP_ARRAY(name); + FREE_C_HEAP_ARRAY(options); } } else if (match_option(option, "--add-reads=", &tail)) { if (!create_numbered_module_property("jdk.module.addreads", tail, addreads_count++)) { @@ -2333,7 +2331,7 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, JVMFlagOrigin char *options = NEW_C_HEAP_ARRAY(char, length, mtArguments); jio_snprintf(options, length, "%s", tail); JvmtiAgentList::add("instrument", options, false); - FREE_C_HEAP_ARRAY(char, options); + FREE_C_HEAP_ARRAY(options); // java agents need module java.instrument if (!create_numbered_module_property("jdk.module.addmods", "java.instrument", _addmods_count++)) { @@ -3068,7 +3066,7 @@ class ScopedVMInitArgs : public StackObj { for (int i = 0; i < _args.nOptions; i++) { os::free(_args.options[i].optionString); } - FREE_C_HEAP_ARRAY(JavaVMOption, _args.options); + FREE_C_HEAP_ARRAY(_args.options); } // Insert options into this option list, to replace option at @@ -3217,7 +3215,7 @@ jint Arguments::parse_vm_options_file(const char* file_name, ScopedVMInitArgs* v ssize_t bytes_read = ::read(fd, (void *)buf, (unsigned)bytes_alloc); ::close(fd); if (bytes_read < 0) { - FREE_C_HEAP_ARRAY(char, buf); + FREE_C_HEAP_ARRAY(buf); jio_fprintf(defaultStream::error_stream(), "Could not read options file '%s'\n", file_name); return JNI_ERR; @@ -3225,13 +3223,13 @@ jint Arguments::parse_vm_options_file(const char* file_name, ScopedVMInitArgs* v if (bytes_read == 0) { // tell caller there is no option data and that is ok - FREE_C_HEAP_ARRAY(char, buf); + FREE_C_HEAP_ARRAY(buf); return JNI_OK; } retcode = parse_options_buffer(file_name, buf, bytes_read, vm_args); - FREE_C_HEAP_ARRAY(char, buf); + FREE_C_HEAP_ARRAY(buf); return retcode; } @@ -3564,7 +3562,7 @@ jint Arguments::parse(const JavaVMInitArgs* initial_cmd_args) { char *vmoptions = ClassLoader::lookup_vm_options(); if (vmoptions != nullptr) { code = parse_options_buffer("vm options resource", vmoptions, strlen(vmoptions), &initial_vm_options_args); - FREE_C_HEAP_ARRAY(char, vmoptions); + FREE_C_HEAP_ARRAY(vmoptions); if (code != JNI_OK) { return code; } @@ -3782,10 +3780,6 @@ jint Arguments::parse(const JavaVMInitArgs* initial_cmd_args) { void Arguments::set_compact_headers_flags() { #ifdef _LP64 - if (UseCompactObjectHeaders && FLAG_IS_CMDLINE(UseCompressedClassPointers) && !UseCompressedClassPointers) { - warning("Compact object headers require compressed class pointers. Disabling compact object headers."); - FLAG_SET_DEFAULT(UseCompactObjectHeaders, false); - } if (UseCompactObjectHeaders && !UseObjectMonitorTable) { // If UseCompactObjectHeaders is on the command line, turn on UseObjectMonitorTable. if (FLAG_IS_CMDLINE(UseCompactObjectHeaders)) { @@ -3799,9 +3793,6 @@ void Arguments::set_compact_headers_flags() { FLAG_SET_DEFAULT(UseObjectMonitorTable, true); } } - if (UseCompactObjectHeaders && !UseCompressedClassPointers) { - FLAG_SET_DEFAULT(UseCompressedClassPointers, true); - } #endif } @@ -3817,9 +3808,7 @@ jint Arguments::apply_ergo() { set_compact_headers_flags(); - if (UseCompressedClassPointers) { - CompressedKlassPointers::pre_initialize(); - } + CompressedKlassPointers::pre_initialize(); CDSConfig::ergo_initialize(); @@ -3864,10 +3853,6 @@ jint Arguments::apply_ergo() { DebugNonSafepoints = true; } - if (FLAG_IS_CMDLINE(CompressedClassSpaceSize) && !UseCompressedClassPointers) { - warning("Setting CompressedClassSpaceSize has no effect when compressed class pointers are not used"); - } - // Treat the odd case where local verification is enabled but remote // verification is not as if both were enabled. if (BytecodeVerificationLocal && !BytecodeVerificationRemote) { diff --git a/src/hotspot/share/runtime/deoptimization.cpp b/src/hotspot/share/runtime/deoptimization.cpp index 5fbe2842a2b..9db9c676268 100644 --- a/src/hotspot/share/runtime/deoptimization.cpp +++ b/src/hotspot/share/runtime/deoptimization.cpp @@ -249,9 +249,9 @@ Deoptimization::UnrollBlock::UnrollBlock(int size_of_deoptimized_frame, } Deoptimization::UnrollBlock::~UnrollBlock() { - FREE_C_HEAP_ARRAY(intptr_t, _frame_sizes); - FREE_C_HEAP_ARRAY(intptr_t, _frame_pcs); - FREE_C_HEAP_ARRAY(intptr_t, _register_block); + FREE_C_HEAP_ARRAY(_frame_sizes); + FREE_C_HEAP_ARRAY(_frame_pcs); + FREE_C_HEAP_ARRAY(_register_block); } int Deoptimization::UnrollBlock::size_of_frames() const { @@ -2021,7 +2021,7 @@ static void post_deoptimization_event(nmethod* nm, #endif // INCLUDE_JFR static void log_deopt(nmethod* nm, Method* tm, intptr_t pc, frame& fr, int trap_bci, - const char* reason_name, const char* reason_action) { + const char* reason_name, const char* reason_action, const char* class_name) { LogTarget(Debug, deoptimization) lt; if (lt.is_enabled()) { LogStream ls(lt); @@ -2035,6 +2035,9 @@ static void log_deopt(nmethod* nm, Method* tm, intptr_t pc, frame& fr, int trap_ } ls.print("%s ", reason_name); ls.print("%s ", reason_action); + if (class_name != nullptr) { + ls.print("%s ", class_name); + } ls.print_cr("pc=" INTPTR_FORMAT " relative_pc=" INTPTR_FORMAT, pc, fr.pc() - nm->code_begin()); } @@ -2135,6 +2138,17 @@ JRT_ENTRY(void, Deoptimization::uncommon_trap_inner(JavaThread* current, jint tr MethodData* trap_mdo = get_method_data(current, profiled_method, create_if_missing); + Symbol* class_name = nullptr; + bool unresolved = false; + if (unloaded_class_index >= 0) { + constantPoolHandle constants (current, trap_method->constants()); + if (constants->tag_at(unloaded_class_index).is_unresolved_klass()) { + class_name = constants->klass_name_at(unloaded_class_index); + unresolved = true; + } else if (constants->tag_at(unloaded_class_index).is_symbol()) { + class_name = constants->symbol_at(unloaded_class_index); + } + } { // Log Deoptimization event for JFR, UL and event system Method* tm = trap_method(); const char* reason_name = trap_reason_name(reason); @@ -2142,10 +2156,24 @@ JRT_ENTRY(void, Deoptimization::uncommon_trap_inner(JavaThread* current, jint tr intptr_t pc = p2i(fr.pc()); JFR_ONLY(post_deoptimization_event(nm, tm, trap_bci, trap_bc, reason, action);) - log_deopt(nm, tm, pc, fr, trap_bci, reason_name, reason_action); - Events::log_deopt_message(current, "Uncommon trap: reason=%s action=%s pc=" INTPTR_FORMAT " method=%s @ %d %s", + + ResourceMark rm; + + const char* class_name_str = nullptr; + const char* class_name_msg = nullptr; + stringStream st, stm; + if (class_name != nullptr) { + class_name->print_symbol_on(&st); + class_name_str = st.freeze(); + stm.print("class=%s ", class_name_str); + class_name_msg = stm.freeze(); + } else { + class_name_msg = ""; + } + log_deopt(nm, tm, pc, fr, trap_bci, reason_name, reason_action, class_name_str); + Events::log_deopt_message(current, "Uncommon trap: reason=%s action=%s pc=" INTPTR_FORMAT " method=%s @ %d %s%s", reason_name, reason_action, pc, - tm->name_and_sig_as_C_string(), trap_bci, nm->compiler_name()); + tm->name_and_sig_as_C_string(), trap_bci, class_name_msg, nm->compiler_name()); } // Print a bunch of diagnostics, if requested. @@ -2173,20 +2201,13 @@ JRT_ENTRY(void, Deoptimization::uncommon_trap_inner(JavaThread* current, jint tr #endif nm->log_identity(xtty); } - Symbol* class_name = nullptr; - bool unresolved = false; - if (unloaded_class_index >= 0) { - constantPoolHandle constants (current, trap_method->constants()); - if (constants->tag_at(unloaded_class_index).is_unresolved_klass()) { - class_name = constants->klass_name_at(unloaded_class_index); - unresolved = true; - if (xtty != nullptr) + if (class_name != nullptr) { + if (xtty != nullptr) { + if (unresolved) { xtty->print(" unresolved='1'"); - } else if (constants->tag_at(unloaded_class_index).is_symbol()) { - class_name = constants->symbol_at(unloaded_class_index); - } - if (xtty != nullptr) + } xtty->name(class_name); + } } if (xtty != nullptr && trap_mdo != nullptr && (int)reason < (int)MethodData::_trap_hist_limit) { // Dump the relevant MDO state. diff --git a/src/hotspot/share/runtime/flags/jvmFlag.cpp b/src/hotspot/share/runtime/flags/jvmFlag.cpp index 405b47e1813..1c965360599 100644 --- a/src/hotspot/share/runtime/flags/jvmFlag.cpp +++ b/src/hotspot/share/runtime/flags/jvmFlag.cpp @@ -648,7 +648,7 @@ void JVMFlag::printSetFlags(outputStream* out) { } } out->cr(); - FREE_C_HEAP_ARRAY(JVMFlag*, array); + FREE_C_HEAP_ARRAY(array); } #ifndef PRODUCT diff --git a/src/hotspot/share/runtime/flags/jvmFlagAccess.cpp b/src/hotspot/share/runtime/flags/jvmFlagAccess.cpp index 2db9f198ddd..8efa8f2de62 100644 --- a/src/hotspot/share/runtime/flags/jvmFlagAccess.cpp +++ b/src/hotspot/share/runtime/flags/jvmFlagAccess.cpp @@ -329,7 +329,7 @@ JVMFlag::Error JVMFlagAccess::set_ccstr(JVMFlag* flag, ccstr* value, JVMFlagOrig flag->set_ccstr(new_value); if (!flag->is_default() && old_value != nullptr) { // Old value is heap allocated so free it. - FREE_C_HEAP_ARRAY(char, old_value); + FREE_C_HEAP_ARRAY(old_value); } // Unlike the other APIs, the old value is NOT returned, so the caller won't need to free it. // The callers typically don't care what the old value is. diff --git a/src/hotspot/share/runtime/flags/jvmFlagConstraintsRuntime.cpp b/src/hotspot/share/runtime/flags/jvmFlagConstraintsRuntime.cpp index 1e6efd893c8..1f16fada239 100644 --- a/src/hotspot/share/runtime/flags/jvmFlagConstraintsRuntime.cpp +++ b/src/hotspot/share/runtime/flags/jvmFlagConstraintsRuntime.cpp @@ -153,6 +153,20 @@ JVMFlag::Error OnSpinWaitInstNameConstraintFunc(ccstr value, bool verbose) { return JVMFlag::VIOLATES_CONSTRAINT; } +#ifdef LINUX + if (strcmp(value, "wfet") == 0) { + if (UnlockExperimentalVMOptions) { + return JVMFlag::SUCCESS; + } else { + JVMFlag::printError(verbose, + "'wfet' value for OnSpinWaitInst is experimental and " + "must be enabled via -XX:+UnlockExperimentalVMOptions.\n" + "Error: The unlock option must precede 'OnSpinWaitInst'.\n"); + return JVMFlag::VIOLATES_CONSTRAINT; + } + } +#endif + if (strcmp(value, "nop") != 0 && strcmp(value, "isb") != 0 && strcmp(value, "yield") != 0 && @@ -160,7 +174,7 @@ JVMFlag::Error OnSpinWaitInstNameConstraintFunc(ccstr value, bool verbose) { strcmp(value, "none") != 0) { JVMFlag::printError(verbose, "Unrecognized value %s for OnSpinWaitInst. Must be one of the following: " - "nop, isb, yield, sb, none\n", + "nop, isb, yield, sb," LINUX_ONLY(" wfet,") " none\n", value); return JVMFlag::VIOLATES_CONSTRAINT; } diff --git a/src/hotspot/share/runtime/frame.cpp b/src/hotspot/share/runtime/frame.cpp index 8f969600ba8..d691a3c8028 100644 --- a/src/hotspot/share/runtime/frame.cpp +++ b/src/hotspot/share/runtime/frame.cpp @@ -1286,7 +1286,7 @@ public: } bool is_good(oop* p) { - return *p == nullptr || (dbg_is_safe(*p, -1) && dbg_is_safe((*p)->klass(), -1) && oopDesc::is_oop_or_null(*p)); + return *p == nullptr || (dbg_is_safe(*p, -1) && dbg_is_safe((*p)->klass_without_asserts(), -1) && oopDesc::is_oop_or_null(*p)); } void describe(FrameValues& values, int frame_no) { for (int i = 0; i < _oops->length(); i++) { diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index 17e10e2f87c..9d38a44cbd5 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -871,6 +871,9 @@ const int ObjectAlignmentInBytes = 8; develop(bool, TimeOopMap, false, \ "Time calls to GenerateOopMap::compute_map() in sum") \ \ + develop(bool, GenerateOopMapALot, false, \ + "Generate interpreter oopmaps at all safepoints") \ + \ develop(bool, TraceFinalizerRegistration, false, \ "Trace registration of final references") \ \ @@ -1368,9 +1371,6 @@ const int ObjectAlignmentInBytes = 8; "Maximum size of Metaspaces (in bytes)") \ constraint(MaxMetaspaceSizeConstraintFunc,AfterErgo) \ \ - product(bool, UseCompressedClassPointers, true, \ - "(Deprecated) Use 32-bit class pointers.") \ - \ product(size_t, CompressedClassSpaceSize, 1*G, \ "Maximum size of class area in Metaspace when compressed " \ "class pointers are used") \ @@ -1517,6 +1517,10 @@ const int ObjectAlignmentInBytes = 8; "Size of code heap with non-nmethods (in bytes)") \ constraint(VMPageSizeConstraintFunc, AtParse) \ \ + product(size_t, HotCodeHeapSize, 0, EXPERIMENTAL, \ + "Size of code heap with predicted hot methods (in bytes)") \ + range(0, SIZE_MAX) \ + \ product_pd(size_t, CodeCacheExpansionSize, \ "Code cache expansion size (in bytes)") \ range(32*K, SIZE_MAX) \ @@ -1929,7 +1933,7 @@ const int ObjectAlignmentInBytes = 8; "Mark all threads after a safepoint, and clear on a modify " \ "fence. Add cleanliness checks.") \ \ - product(bool, UseObjectMonitorTable, false, DIAGNOSTIC, \ + product(bool, UseObjectMonitorTable, true, DIAGNOSTIC, \ "Use a table to record inflated monitors rather than the first " \ "word of the object.") \ \ diff --git a/src/hotspot/share/runtime/hotCodeCollector.cpp b/src/hotspot/share/runtime/hotCodeCollector.cpp new file mode 100644 index 00000000000..6bdeee011ce --- /dev/null +++ b/src/hotspot/share/runtime/hotCodeCollector.cpp @@ -0,0 +1,259 @@ +/* + * Copyright Amazon.com Inc. 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. + * + */ + +#ifdef COMPILER2 + +#include "code/codeCache.hpp" +#include "code/compiledIC.hpp" +#include "compiler/compilerDefinitions.inline.hpp" +#include "logging/log.hpp" +#include "memory/resourceArea.hpp" +#include "oops/method.inline.hpp" +#include "runtime/hotCodeCollector.hpp" +#include "runtime/hotCodeSampler.hpp" +#include "runtime/java.hpp" +#include "runtime/javaThread.inline.hpp" + +// Initialize static variables +bool HotCodeCollector::_is_initialized = false; +int HotCodeCollector::_new_c2_nmethods_count = 0; +int HotCodeCollector::_total_c2_nmethods_count = 0; + +HotCodeCollector::HotCodeCollector() : JavaThread(thread_entry) {} + +void HotCodeCollector::initialize() { + EXCEPTION_MARK; + + assert(HotCodeHeap, "HotCodeCollector requires HotCodeHeap enabled"); + assert(CompilerConfig::is_c2_enabled(), "HotCodeCollector requires C2 enabled"); + assert(NMethodRelocation, "HotCodeCollector requires NMethodRelocation enabled"); + assert(HotCodeHeapSize > 0, "HotCodeHeapSize must be non-zero to use HotCodeCollector"); + assert(CodeCache::get_code_heap(CodeBlobType::MethodHot) != nullptr, "MethodHot code heap not found"); + + Handle thread_oop = JavaThread::create_system_thread_object("HotCodeCollectorThread", CHECK); + HotCodeCollector* thread = new HotCodeCollector(); + JavaThread::vm_exit_on_osthread_failure(thread); + JavaThread::start_internal_daemon(THREAD, thread, thread_oop, NormPriority); + + _is_initialized = true; +} + +bool HotCodeCollector::is_nmethod_count_stable() { + if (HotCodeStablePercent < 0) { + log_info(hotcode)("HotCodeStablePercent is less than zero, stable check disabled"); + return true; + } + + MutexLocker ml_CodeCache_lock(CodeCache_lock, Mutex::_no_safepoint_check_flag); + + if (_total_c2_nmethods_count <= 0) { + log_info(hotcode)("No registered C2 nmethods"); + return false; + } + + const double percent_new = 100.0 * _new_c2_nmethods_count / _total_c2_nmethods_count; + bool is_stable_nmethod_count = percent_new <= HotCodeStablePercent; + + log_info(hotcode)("C2 nmethod count %s", is_stable_nmethod_count ? "stable" : "not stable"); + log_debug(hotcode)("C2 nmethod stats: New: %d, Total: %d, Percent new: %f", _new_c2_nmethods_count, _total_c2_nmethods_count, percent_new); + + _new_c2_nmethods_count = 0; + + return is_stable_nmethod_count; +} + +void HotCodeCollector::thread_entry(JavaThread* thread, TRAPS) { + // Initial sleep to allow JVM to warm up + thread->sleep(HotCodeStartupDelaySeconds * 1000); + + while (true) { + ResourceMark rm; + + // Sample application and group hot nmethods if nmethod count is stable + if (is_nmethod_count_stable()) { + log_info(hotcode)("Sampling..."); + + ThreadSampler sampler; + uint64_t start_time = os::javaTimeMillis(); + while (os::javaTimeMillis() - start_time <= HotCodeSampleSeconds * 1000) { + sampler.sample_all_java_threads(); + thread->sleep(rand_sampling_period_ms()); + } + + Candidates candidates(sampler); + do_grouping(candidates); + } + + thread->sleep(HotCodeIntervalSeconds * 1000); + } +} + +void HotCodeCollector::do_grouping(Candidates& candidates) { + int num_relocated = 0; + + // Sort nmethods by increasing sample count so pop() returns the hottest + candidates.sort(); + + while (candidates.has_candidates()) { + + double percent_from_hot = candidates.get_hot_sample_percent(); + log_debug(hotcode)("Percentage of samples from hot code heap: %f", percent_from_hot); + if (percent_from_hot >= HotCodeSamplePercent) { + log_info(hotcode)("Percentage of samples from hot nmethods over threshold. Done collecting hot code"); + break; + } + + nmethod* candidate = candidates.get_candidate(); + + 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); + + num_relocated += do_relocation(candidate, 0); + } + + log_info(hotcode)("Collection done. Relocated %d nmethods to the MethodHot heap", num_relocated); +} + +int HotCodeCollector::do_relocation(void* candidate, uint call_level) { + if (candidate == nullptr) { + return 0; + } + + // Verify that address still points to CodeBlob + CodeBlob* blob = CodeCache::find_blob(candidate); + if (blob == nullptr) { + return 0; + } + + // Verify that blob is nmethod + nmethod* nm = blob->as_nmethod_or_null(); + if (nm == nullptr || nm->method() == nullptr) { + return 0; + } + + // The candidate may have been recompiled or already relocated. + // Retrieve the latest nmethod from the Method + nm = nm->method()->code(); + + // Verify the nmethod is still valid for relocation + if (nm == nullptr || !nm->is_in_use() || !nm->is_compiled_by_c2()) { + return 0; + } + + // Verify code heap has space + if (CodeCache::get_code_heap(CodeBlobType::MethodHot)->unallocated_capacity() < (size_t)nm->size()) { + log_info(hotcode)("Not enough free space in MethodHot heap (%zd bytes) to relocate nm (%d bytes). Bailing out", + CodeCache::get_code_heap(CodeBlobType::MethodHot)->unallocated_capacity(), nm->size()); + return 0; + } + + // Number of nmethods relocated (candidate + callees) + int num_relocated = 0; + + // Pointer to nmethod in hot heap + nmethod* hot_nm = nullptr; + + if (CodeCache::get_code_blob_type(nm) != CodeBlobType::MethodHot) { + CompiledICLocker ic_locker(nm); + hot_nm = nm->relocate(CodeBlobType::MethodHot); + + if (hot_nm != nullptr) { + // Successfully relocated nmethod. Update counts and proceed to callee relocation. + log_debug(hotcode)("Successful relocation: nmethod (%p), method (%s), call level (%d)", nm, hot_nm->method()->name_and_sig_as_C_string(), call_level); + num_relocated++; + } else { + // Relocation failed so return and do not attempt to relocate callees + log_debug(hotcode)("Failed relocation: nmethod (%p), call level (%d)", nm, call_level); + return 0; + } + } else { + // Skip relocation since already in hot heap, but still relocate callees + // since they may not have been compiled when this method was first relocated + log_debug(hotcode)("Already relocated: nmethod (%p), method (%s), call level (%d)", nm, nm->method()->name_and_sig_as_C_string(), call_level); + hot_nm = nm; + } + + assert(hot_nm != nullptr, "unable to relocate callees"); + + if (call_level < HotCodeCallLevel) { + // Loop over relocations to relocate callees + RelocIterator relocIter(hot_nm); + while (relocIter.next()) { + // Check if this is a call + Relocation* reloc = relocIter.reloc(); + if (!reloc->is_call()) { + continue; + } + + // Find the call destination address + address dest = ((CallRelocation*) reloc)->destination(); + + // Recursively relocate callees + num_relocated += do_relocation(dest, call_level + 1); + } + } + + return num_relocated; +} + +void HotCodeCollector::unregister_nmethod(nmethod* nm) { + assert_lock_strong(CodeCache_lock); + if (!_is_initialized) { + return; + } + + if (!nm->is_compiled_by_c2()) { + return; + } + + if (CodeCache::get_code_blob_type(nm) == CodeBlobType::MethodHot) { + // Nmethods in the hot code heap do not count towards total C2 nmethods. + return; + } + + // CodeCache_lock is held, so we can safely decrement the count. + _total_c2_nmethods_count--; +} + +void HotCodeCollector::register_nmethod(nmethod* nm) { + assert_lock_strong(CodeCache_lock); + if (!_is_initialized) { + return; + } + + if (!nm->is_compiled_by_c2()) { + return; // Only C2 nmethods are relocated to HotCodeHeap. + } + + if (CodeCache::get_code_blob_type(nm) == CodeBlobType::MethodHot) { + // Nmethods in the hot code heap do not count towards total C2 nmethods. + return; + } + + // CodeCache_lock is held, so we can safely increment the count. + _new_c2_nmethods_count++; + _total_c2_nmethods_count++; +} +#endif // COMPILER2 diff --git a/src/hotspot/share/runtime/hotCodeCollector.hpp b/src/hotspot/share/runtime/hotCodeCollector.hpp new file mode 100644 index 00000000000..dbefa3dc788 --- /dev/null +++ b/src/hotspot/share/runtime/hotCodeCollector.hpp @@ -0,0 +1,56 @@ +/* + * Copyright Amazon.com Inc. 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. + * + */ + +#ifdef COMPILER2 +#ifndef SHARE_RUNTIME_HOTCODECOLLECTOR_HPP +#define SHARE_RUNTIME_HOTCODECOLLECTOR_HPP + +#include "runtime/javaThread.hpp" + +class Candidates; + +class HotCodeCollector : public JavaThread { + private: + static bool _is_initialized; + + static int _new_c2_nmethods_count; + static int _total_c2_nmethods_count; + + HotCodeCollector(); + + static void do_grouping(Candidates& candidates); + + static int do_relocation(void* candidate, uint call_level); + + public: + static void initialize(); + static void thread_entry(JavaThread* thread, TRAPS); + static void unregister_nmethod(nmethod* nm); + static void register_nmethod(nmethod* nm); + + static bool is_nmethod_count_stable(); +}; + +#endif // SHARE_RUNTIME_HOTCODECOLLECTOR_HPP +#endif // COMPILER2 diff --git a/src/hotspot/share/runtime/hotCodeSampler.cpp b/src/hotspot/share/runtime/hotCodeSampler.cpp new file mode 100644 index 00000000000..730a47d238a --- /dev/null +++ b/src/hotspot/share/runtime/hotCodeSampler.cpp @@ -0,0 +1,121 @@ +/* + * Copyright Amazon.com Inc. 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. + * + */ + +#ifdef COMPILER2 + +#include "code/codeCache.hpp" +#include "logging/log.hpp" +#include "runtime/hotCodeSampler.hpp" +#include "runtime/javaThread.inline.hpp" + +void ThreadSampler::sample_all_java_threads() { + // Collect samples for each JavaThread + for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) { + if (jt->is_hidden_from_external_view() || + jt->in_deopt_handler() || + (jt->thread_state() != _thread_in_native && jt->thread_state() != _thread_in_Java)) { + continue; + } + + GetPCTask task(jt); + task.run(); + address pc = task.pc(); + if (pc == nullptr) { + continue; + } + + if (CodeCache::contains(pc)) { + nmethod* nm = CodeCache::find_blob(pc)->as_nmethod_or_null(); + if (nm != nullptr) { + bool created = false; + int *count = _samples.put_if_absent(nm, 0, &created); + (*count)++; + if (created) { + _samples.maybe_grow(); + } + } + } + } +} + +Candidates::Candidates(ThreadSampler& sampler) + : _hot_sample_count(0), _non_profiled_sample_count(0) { + auto func = [&](nmethod* nm, int count) { + if (CodeCache::get_code_blob_type(nm) == CodeBlobType::MethodNonProfiled) { + _candidates.append(Pair(nm, count)); + add_non_profiled_sample_count(count); + } else if (CodeCache::get_code_blob_type(nm) == CodeBlobType::MethodHot) { + add_hot_sample_count(count); + } + }; + sampler.iterate_samples(func); + + log_info(hotcode)("Generated candidate list from %d samples corresponding to %d nmethods", _non_profiled_sample_count + _hot_sample_count, _candidates.length()); +} + +void Candidates::add_candidate(nmethod* nm, int count) { + _candidates.append(Pair(nm, count)); +} + +void Candidates::add_hot_sample_count(int count) { + _hot_sample_count += count; +} + +void Candidates::add_non_profiled_sample_count(int count) { + _non_profiled_sample_count += count; +} + +void Candidates::sort() { + _candidates.sort( + [](Pair* a, Pair* b) { + if (a->second > b->second) return 1; + if (a->second < b->second) return -1; + return 0; + } + ); +} + +bool Candidates::has_candidates() { + return !_candidates.is_empty(); +} + +nmethod* Candidates::get_candidate() { + assert(has_candidates(), "must not be empty"); + Pair candidate = _candidates.pop(); + + _hot_sample_count += candidate.second; + _non_profiled_sample_count -= candidate.second; + + return candidate.first; +} + +double Candidates::get_hot_sample_percent() { + if (_hot_sample_count + _non_profiled_sample_count == 0) { + return 0; + } + + return 100.0 * _hot_sample_count / (_hot_sample_count + _non_profiled_sample_count); +} + +#endif // COMPILER2 diff --git a/src/hotspot/share/runtime/hotCodeSampler.hpp b/src/hotspot/share/runtime/hotCodeSampler.hpp new file mode 100644 index 00000000000..d61cac791e1 --- /dev/null +++ b/src/hotspot/share/runtime/hotCodeSampler.hpp @@ -0,0 +1,104 @@ +/* + * Copyright Amazon.com Inc. 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. + * + */ + +#ifdef COMPILER2 +#ifndef SHARE_RUNTIME_HOTCODESAMPLER_HPP +#define SHARE_RUNTIME_HOTCODESAMPLER_HPP + +#include "runtime/javaThread.hpp" +#include "runtime/suspendedThreadTask.hpp" +#include "runtime/threadSMR.hpp" +#include "utilities/pair.hpp" +#include "utilities/resizableHashTable.hpp" + +// Generate a random sampling period between min and max +static inline uint rand_sampling_period_ms() { + assert(HotCodeMaxSamplingMs >= HotCodeMinSamplingMs, "max cannot be smaller than min"); + julong range = (julong)HotCodeMaxSamplingMs - (julong)HotCodeMinSamplingMs + 1; + return (uint)(os::random() % range) + HotCodeMinSamplingMs; +} + +class ThreadSampler; + +class Candidates : public StackObj { + private: + GrowableArray> _candidates; + int _hot_sample_count; + int _non_profiled_sample_count; + + public: + Candidates(ThreadSampler& sampler); + + void add_candidate(nmethod* nm, int count); + void add_hot_sample_count(int count); + void add_non_profiled_sample_count(int count); + void sort(); + + bool has_candidates(); + nmethod* get_candidate(); + double get_hot_sample_percent(); +}; + +class GetPCTask : public SuspendedThreadTask { + private: + address _pc; + + void do_task(const SuspendedThreadTaskContext& context) override { + JavaThread* jt = JavaThread::cast(context.thread()); + if (jt->thread_state() != _thread_in_native && jt->thread_state() != _thread_in_Java) { + return; + } + _pc = os::fetch_frame_from_context(context.ucontext(), nullptr, nullptr); + } + + public: + GetPCTask(JavaThread* thread) : SuspendedThreadTask(thread), _pc(nullptr) {} + + address pc() const { + return _pc; + } +}; + +class ThreadSampler : public StackObj { + private: + static const int INITIAL_TABLE_SIZE = 109; + + // Table of nmethods found during profiling with sample count + ResizeableHashTable _samples; + + public: + ThreadSampler() : _samples(INITIAL_TABLE_SIZE, HotCodeSampleSeconds * 1000 / HotCodeMaxSamplingMs) {} + + // Iterate over and sample all Java threads + void sample_all_java_threads(); + + // Iterate over all samples with a callback function + template + void iterate_samples(Function func) { + _samples.iterate_all(func); + } +}; + +#endif // SHARE_RUNTIME_HOTCODESAMPLER_HPP +#endif // COMPILER2 diff --git a/src/hotspot/share/runtime/icache.hpp b/src/hotspot/share/runtime/icache.hpp index bc153862323..692a876d9a6 100644 --- a/src/hotspot/share/runtime/icache.hpp +++ b/src/hotspot/share/runtime/icache.hpp @@ -129,4 +129,27 @@ class ICacheStubGenerator : public StubCodeGenerator { void generate_icache_flush(ICache::flush_icache_stub_t* flush_icache_stub); }; +class DefaultICacheInvalidationContext : StackObj { + public: + NONCOPYABLE(DefaultICacheInvalidationContext); + + DefaultICacheInvalidationContext() {} + + ~DefaultICacheInvalidationContext() {} + + void set_has_modified_code() {} +}; + +#ifndef PD_ICACHE_INVALIDATION_CONTEXT +#define PD_ICACHE_INVALIDATION_CONTEXT DefaultICacheInvalidationContext +#endif // PD_ICACHE_INVALIDATION_CONTEXT + +class ICacheInvalidationContext final : public PD_ICACHE_INVALIDATION_CONTEXT { + private: + NONCOPYABLE(ICacheInvalidationContext); + + public: + using PD_ICACHE_INVALIDATION_CONTEXT::PD_ICACHE_INVALIDATION_CONTEXT; +}; + #endif // SHARE_RUNTIME_ICACHE_HPP diff --git a/src/hotspot/share/runtime/init.cpp b/src/hotspot/share/runtime/init.cpp index adc49f84358..d820968495e 100644 --- a/src/hotspot/share/runtime/init.cpp +++ b/src/hotspot/share/runtime/init.cpp @@ -70,6 +70,10 @@ void VM_Version_init(); void icache_init2(); void initialize_stub_info(); // must precede all blob/stub generation void preuniverse_stubs_init(); + +#if INCLUDE_CDS +void stubs_AOTAddressTable_init(); +#endif // INCLUDE_CDS void initial_stubs_init(); jint universe_init(); // depends on codeCache_init and preuniverse_stubs_init @@ -149,13 +153,19 @@ jint init_globals() { AOTCodeCache::init2(); // depends on universe_init, must be before initial_stubs_init AsyncLogWriter::initialize(); +#if INCLUDE_CDS + stubs_AOTAddressTable_init(); // publish external addresses used by stubs + // depends on AOTCodeCache::init2 +#endif // INCLUDE_CDS initial_stubs_init(); // stubgen initial stub routines // stack overflow exception blob is referenced by the interpreter - AOTCodeCache::init_early_stubs_table(); // need this after stubgen initial stubs and before shared runtime initial stubs SharedRuntime::generate_initial_stubs(); gc_barrier_stubs_init(); // depends on universe_init, must be before interpreter_init continuations_init(); // must precede continuation stub generation - continuation_stubs_init(); // depends on continuations_init + AOTCodeCache::init3(); // depends on stubs_AOTAddressTable_init + // and continuations_init and must + // precede continuation stub generation + continuation_stubs_init(); // depends on continuations_init and AOTCodeCache::init3 #if INCLUDE_JFR SharedRuntime::generate_jfr_stubs(); #endif @@ -164,7 +174,6 @@ jint init_globals() { InterfaceSupport_init(); VMRegImpl::set_regName(); // need this before generate_stubs (for printing oop maps). SharedRuntime::generate_stubs(); - AOTCodeCache::init_shared_blobs_table(); // need this after generate_stubs SharedRuntime::init_adapter_library(); // do this after AOTCodeCache::init_shared_blobs_table return JNI_OK; } diff --git a/src/hotspot/share/runtime/interfaceSupport.cpp b/src/hotspot/share/runtime/interfaceSupport.cpp index 11a7d9fd41f..6ccf63b4c5e 100644 --- a/src/hotspot/share/runtime/interfaceSupport.cpp +++ b/src/hotspot/share/runtime/interfaceSupport.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -23,6 +23,7 @@ */ #include "gc/shared/collectedHeap.inline.hpp" +#include "interpreter/interpreterRuntime.hpp" #include "logging/log.hpp" #include "memory/resourceArea.hpp" #include "memory/universe.hpp" @@ -65,6 +66,10 @@ VMEntryWrapper::~VMEntryWrapper() { if (VerifyStack) { InterfaceSupport::verify_stack(); } + // Verify interpreter oopmap generation + if (GenerateOopMapALot) { + InterpreterRuntime::generate_oop_map_alot(); + } } VMNativeEntryWrapper::VMNativeEntryWrapper() { diff --git a/src/hotspot/share/runtime/javaThread.cpp b/src/hotspot/share/runtime/javaThread.cpp index 77a567d4a09..c96ec9a7c0d 100644 --- a/src/hotspot/share/runtime/javaThread.cpp +++ b/src/hotspot/share/runtime/javaThread.cpp @@ -308,7 +308,7 @@ static jlong* resize_counters_array(jlong* old_counters, int current_size, int n if (new_size > current_size) { memset(new_counters + current_size, 0, sizeof(jlong) * (new_size - current_size)); } - FREE_C_HEAP_ARRAY(jlong, old_counters); + FREE_C_HEAP_ARRAY(old_counters); } return new_counters; } @@ -700,7 +700,7 @@ JavaThread::~JavaThread() { #if INCLUDE_JVMCI if (JVMCICounterSize > 0) { - FREE_C_HEAP_ARRAY(jlong, _jvmci_counters); + FREE_C_HEAP_ARRAY(_jvmci_counters); } #endif // INCLUDE_JVMCI } @@ -1884,7 +1884,7 @@ WordSize JavaThread::popframe_preserved_args_size_in_words() { void JavaThread::popframe_free_preserved_args() { assert(_popframe_preserved_args != nullptr, "should not free PopFrame preserved arguments twice"); - FREE_C_HEAP_ARRAY(char, (char*)_popframe_preserved_args); + FREE_C_HEAP_ARRAY((char*)_popframe_preserved_args); _popframe_preserved_args = nullptr; _popframe_preserved_args_size = 0; } diff --git a/src/hotspot/share/runtime/mutex.cpp b/src/hotspot/share/runtime/mutex.cpp index c455b5f36a2..8e0c1d10e5c 100644 --- a/src/hotspot/share/runtime/mutex.cpp +++ b/src/hotspot/share/runtime/mutex.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, 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 @@ -293,7 +293,8 @@ Mutex::Mutex(Rank rank, const char * name, bool allow_vm_block) : _owner(nullptr _rank = rank; _skip_rank_check = false; - assert(_rank >= static_cast(0) && _rank <= safepoint, "Bad lock rank %s: %s", rank_name(), name); + assert(_rank >= static_cast(0) && _rank <= safepoint, "Bad lock rank %d outside [0, %d]: %s", + static_cast(rank), static_cast(safepoint), name); // The allow_vm_block also includes allowing other non-Java threads to block or // allowing Java threads to block in native. @@ -324,25 +325,33 @@ static const char* _rank_names[] = { "event", "service", "stackwatermark", "tty" static const int _num_ranks = 7; -static const char* rank_name_internal(Mutex::Rank r) { +static void print_rank_name_internal(outputStream* st, Mutex::Rank r) { // Find closest rank and print out the name - stringStream st; for (int i = 0; i < _num_ranks; i++) { if (r == _ranks[i]) { - return _rank_names[i]; + st->print("%s", _rank_names[i]); } else if (r > _ranks[i] && (i < _num_ranks-1 && r < _ranks[i+1])) { int delta = static_cast(_ranks[i+1]) - static_cast(r); - st.print("%s-%d", _rank_names[i+1], delta); - return st.as_string(); + st->print("%s-%d", _rank_names[i+1], delta); } } - return "fail"; +} + +// Requires caller to have ResourceMark. +static const char* rank_name_internal(Mutex::Rank r) { + stringStream st; + print_rank_name_internal(&st, r); + return st.as_string(); } const char* Mutex::rank_name() const { return rank_name_internal(_rank); } +// Does not require caller to have ResourceMark. +void Mutex::print_rank_name(outputStream* st) const { + print_rank_name_internal(st, _rank); +} void Mutex::assert_no_overlap(Rank orig, Rank adjusted, int adjust) { int i = 0; @@ -364,7 +373,8 @@ void Mutex::print_on(outputStream* st) const { if (_allow_vm_block) { st->print("%s", " allow_vm_block"); } - DEBUG_ONLY(st->print(" %s", rank_name())); + st->print(" "); + DEBUG_ONLY(print_rank_name(st)); st->cr(); } diff --git a/src/hotspot/share/runtime/mutex.hpp b/src/hotspot/share/runtime/mutex.hpp index cf2b222d2da..4d30a320cbf 100644 --- a/src/hotspot/share/runtime/mutex.hpp +++ b/src/hotspot/share/runtime/mutex.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, 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 @@ -130,9 +130,11 @@ class Mutex : public CHeapObj { return _skip_rank_check; } + const char* rank_name() const; + void print_rank_name(outputStream* st) const; + public: Rank rank() const { return _rank; } - const char* rank_name() const; Mutex* next() const { return _next; } #endif // ASSERT diff --git a/src/hotspot/share/runtime/mutexLocker.cpp b/src/hotspot/share/runtime/mutexLocker.cpp index b102e6424f1..3dd61374d03 100644 --- a/src/hotspot/share/runtime/mutexLocker.cpp +++ b/src/hotspot/share/runtime/mutexLocker.cpp @@ -105,7 +105,6 @@ Mutex* G1MarkStackFreeList_lock = nullptr; Monitor* G1OldGCCount_lock = nullptr; Mutex* G1OldSets_lock = nullptr; Mutex* G1ReviseYoungLength_lock = nullptr; -Monitor* G1RootRegionScan_lock = nullptr; Mutex* G1RareEvent_lock = nullptr; Mutex* G1Uncommit_lock = nullptr; #endif @@ -216,7 +215,6 @@ void mutex_init() { MUTEX_DEFN(G1MarkStackChunkList_lock , PaddedMutex , nosafepoint); MUTEX_DEFN(G1MarkStackFreeList_lock , PaddedMutex , nosafepoint); MUTEX_DEFN(G1OldSets_lock , PaddedMutex , nosafepoint); - MUTEX_DEFN(G1RootRegionScan_lock , PaddedMonitor, nosafepoint-1); MUTEX_DEFN(G1Uncommit_lock , PaddedMutex , service-2); } #endif diff --git a/src/hotspot/share/runtime/mutexLocker.hpp b/src/hotspot/share/runtime/mutexLocker.hpp index f6c0a967718..682383de401 100644 --- a/src/hotspot/share/runtime/mutexLocker.hpp +++ b/src/hotspot/share/runtime/mutexLocker.hpp @@ -101,7 +101,6 @@ extern Monitor* G1OldGCCount_lock; // in support of "concurrent" f extern Mutex* G1OldSets_lock; // protects the G1 old region sets extern Mutex* G1RareEvent_lock; // Synchronizes (rare) parallel GC operations. extern Mutex* G1ReviseYoungLength_lock; // Protects access to young gen length revising operations. -extern Monitor* G1RootRegionScan_lock; // used to notify that the G1 CM threads have finished scanning the root regions extern Mutex* G1Uncommit_lock; // protects the G1 uncommit list when not at safepoints #endif diff --git a/src/hotspot/share/runtime/nonJavaThread.cpp b/src/hotspot/share/runtime/nonJavaThread.cpp index cb0c3f8910d..e8f01086058 100644 --- a/src/hotspot/share/runtime/nonJavaThread.cpp +++ b/src/hotspot/share/runtime/nonJavaThread.cpp @@ -133,7 +133,7 @@ NamedThread::NamedThread() : {} NamedThread::~NamedThread() { - FREE_C_HEAP_ARRAY(char, _name); + FREE_C_HEAP_ARRAY(_name); } void NamedThread::set_name(const char* format, ...) { diff --git a/src/hotspot/share/runtime/objectMonitor.cpp b/src/hotspot/share/runtime/objectMonitor.cpp index 94399088464..9fc834f4b6b 100644 --- a/src/hotspot/share/runtime/objectMonitor.cpp +++ b/src/hotspot/share/runtime/objectMonitor.cpp @@ -155,7 +155,7 @@ static const jlong MAX_RECHECK_INTERVAL = 1000; // // Succession is provided for by a policy of competitive handoff. // The exiting thread does _not_ grant or pass ownership to the -// successor thread. (This is also referred to as "handoff succession"). +// successor thread. (This is also referred to as "handoff succession"). // Instead the exiting thread releases ownership and possibly wakes // a successor, so the successor can (re)compete for ownership of the lock. // @@ -189,7 +189,7 @@ static const jlong MAX_RECHECK_INTERVAL = 1000; // // Once we have formed a doubly linked list it's easy to find the // successor (A), wake it up, have it remove itself, and update the -// tail pointer, as seen in and 3) below. +// tail pointer, see 3) below. // // 3) entry_list ->F<=>E<=>D<=>C<=>B->null // entry_list_tail ------------------^ @@ -223,7 +223,7 @@ static const jlong MAX_RECHECK_INTERVAL = 1000; // remove itself) or update the tail. // // * The monitor entry list operations avoid locks, but strictly speaking -// they're not lock-free. Enter is lock-free, exit is not. +// they're not lock-free. Enter is lock-free, exit is not. // For a description of 'Methods and apparatus providing non-blocking access // to a resource,' see U.S. Pat. No. 7844973. // @@ -387,7 +387,7 @@ bool ObjectMonitor::try_lock_with_contention_mark(JavaThread* locking_thread, Ob prev_owner = try_set_owner_from(DEFLATER_MARKER, locking_thread); if (prev_owner == DEFLATER_MARKER) { // We successfully cancelled the in-progress async deflation by - // changing owner from DEFLATER_MARKER to current. We now extend + // changing owner from DEFLATER_MARKER to current. We now extend // the lifetime of the contention_mark (e.g. contentions++) here // to prevent the deflater thread from winning the last part of // the 2-part async deflation protocol after the regular @@ -633,14 +633,14 @@ void ObjectMonitor::enter_with_contention_mark(JavaThread* current, ObjectMonito // The thread -- now the owner -- is back in vm mode. // Report the glorious news via TI,DTrace and jvmstat. - // The probe effect is non-trivial. All the reportage occurs + // The probe effect is non-trivial. All the reportage occurs // while we hold the monitor, increasing the length of the critical - // section. Amdahl's parallel speedup law comes vividly into play. + // section. Amdahl's parallel speedup law comes vividly into play. // // Another option might be to aggregate the events (thread local or // per-monitor aggregation) and defer reporting until a more opportune // time -- such as next time some thread encounters contention but has - // yet to acquire the lock. While spinning that thread could + // yet to acquire the lock. While spinning that thread could // spinning we could increment JVMStat counters, etc. DTRACE_MONITOR_PROBE(contended__entered, this, object(), current); @@ -739,11 +739,11 @@ bool ObjectMonitor::try_lock_or_add_to_entry_list(JavaThread* current, ObjectWai return false; } - // Interference - the CAS failed because _entry_list changed. Before + // Interference - the CAS failed because _entry_list changed. Before // retrying the CAS retry taking the lock as it may now be free. if (try_lock(current) == TryLockResult::Success) { - assert(!has_successor(current), "invariant"); assert(has_owner(current), "invariant"); + assert(!has_successor(current), "invariant"); node->TState = ObjectWaiter::TS_RUN; return true; } @@ -953,8 +953,8 @@ bool ObjectMonitor::try_enter_fast(JavaThread* current, ObjectWaiter* current_no // Try the lock - TATAS if (try_lock(current) == TryLockResult::Success) { - assert(!has_successor(current), "invariant"); assert(has_owner(current), "invariant"); + assert(!has_successor(current), "invariant"); return true; } @@ -964,7 +964,7 @@ bool ObjectMonitor::try_enter_fast(JavaThread* current, ObjectWaiter* current_no // // If the _owner is ready but OFFPROC we could use a YieldTo() // operation to donate the remainder of this thread's quantum - // to the owner. This has subtle but beneficial affinity + // to the owner. This has subtle but beneficial affinity // effects. if (try_spin(current)) { @@ -974,15 +974,15 @@ bool ObjectMonitor::try_enter_fast(JavaThread* current, ObjectWaiter* current_no } // The Spin failed -- Enqueue and park the thread ... - assert(!has_successor(current), "invariant"); assert(!has_owner(current), "invariant"); + assert(!has_successor(current), "invariant"); // Enqueue "current" on ObjectMonitor's _entry_list. // // current_node acts as a proxy for current. // As an aside, if were to ever rewrite the synchronization code mostly // in Java, WaitNodes, ObjectMonitors, and Events would become 1st-class - // Java objects. This would avoid awkward lifecycle and liveness issues, + // Java objects. This would avoid awkward lifecycle and liveness issues, // as well as eliminate a subset of ABA issues. // TODO: eliminate ObjectWaiter and enqueue either Threads or Events. @@ -995,7 +995,7 @@ bool ObjectMonitor::try_enter_fast(JavaThread* current, ObjectWaiter* current_no // This thread is now added to the _entry_list. // The lock might have been released while this thread was occupied queueing - // itself onto _entry_list. To close the race and avoid "stranding" and + // itself onto _entry_list. To close the race and avoid "stranding" and // progress-liveness failure the caller must resample-retry _owner before parking. // Note the Dekker/Lamport duality: ST _entry_list; MEMBAR; LD Owner. // In this case the ST-MEMBAR is accomplished with CAS() in try_lock_or_add_to_entry_list. @@ -1051,13 +1051,13 @@ void ObjectMonitor::enter_internal(JavaThread* current, ObjectWaiter* current_no } // Try again, but just so we distinguish between futile wakeups and - // successful wakeups. The following test isn't algorithmically + // successful wakeups. The following test isn't algorithmically // necessary, but it helps us maintain sensible statistics. if (try_lock(current) == TryLockResult::Success) { break; } - // The lock is still contested. + // The lock is still contended. if (!reenter_path) { // Assuming this is not a spurious wakeup we'll normally find _succ == current. @@ -1070,9 +1070,9 @@ void ObjectMonitor::enter_internal(JavaThread* current, ObjectWaiter* current_no } // We can find that we were unpark()ed and redesignated _succ while - // we were spinning. That's harmless. If we iterate and call park(), + // we were spinning. That's harmless. If we iterate and call park(), // park() will consume the event and return immediately and we'll - // just spin again. This pattern can repeat, leaving _succ to simply + // just spin again. This pattern can repeat, leaving _succ to simply // spin on a CPU. if (has_successor(current)) { @@ -1092,9 +1092,9 @@ void ObjectMonitor::enter_internal(JavaThread* current, ObjectWaiter* current_no // Current has acquired the lock -- Unlink current from the _entry_list. unlink_after_acquire(current, current_node); if (has_successor(current)) { - clear_successor(); // Note that we don't need to do OrderAccess::fence() after clearing // _succ here, since we own the lock. + clear_successor(); } // We've acquired ownership with CAS(). @@ -1146,7 +1146,9 @@ bool ObjectMonitor::vthread_monitor_enter(JavaThread* current, ObjectWaiter* wai 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 + if (waiter == nullptr) { + delete node; // for Object.wait() don't delete yet + } dec_unmounted_vthreads(); return true; } @@ -1157,8 +1159,14 @@ bool ObjectMonitor::vthread_monitor_enter(JavaThread* current, ObjectWaiter* wai if (try_lock(current) == TryLockResult::Success) { assert(has_owner(current), "invariant"); unlink_after_acquire(current, node); - if (has_successor(current)) clear_successor(); - if (waiter == nullptr) delete node; // for Object.wait() don't delete yet + if (has_successor(current)) { + // Note that we don't need to do OrderAccess::fence() after clearing + // _succ here, since we own the lock. + clear_successor(); + } + if (waiter == nullptr) { + delete node; // for Object.wait() don't delete yet + } dec_unmounted_vthreads(); return true; } @@ -1182,7 +1190,9 @@ bool ObjectMonitor::resume_operation(JavaThread* current, ObjectWaiter* node, Co if (node->is_wait() && !node->at_reenter()) { bool acquired_monitor = vthread_wait_reenter(current, node, cont); - if (acquired_monitor) return true; + if (acquired_monitor) { + return true; + } } // Retry acquiring monitor... @@ -1196,7 +1206,9 @@ bool ObjectMonitor::resume_operation(JavaThread* current, ObjectWaiter* node, Co } oop vthread = current->vthread(); - if (has_successor(current)) clear_successor(); + if (has_successor(current)) { + clear_successor(); + } // Invariant: after clearing _succ a thread *must* retry acquiring the monitor. OrderAccess::fence(); @@ -1217,7 +1229,11 @@ void ObjectMonitor::vthread_epilog(JavaThread* current, ObjectWaiter* node) { add_to_contentions(-1); dec_unmounted_vthreads(); - if (has_successor(current)) clear_successor(); + if (has_successor(current)) { + // Note that we don't need to do OrderAccess::fence() after clearing + // _succ here, since we own the lock. + clear_successor(); + } guarantee(_recursions == 0, "invariant"); @@ -1419,7 +1435,7 @@ void ObjectMonitor::unlink_after_acquire(JavaThread* current, ObjectWaiter* curr // inopportune) reclamation of "this". // // We'd like to assert that: (THREAD->thread_state() != _thread_blocked) ; -// There's one exception to the claim above, however. enter_internal() can call +// There's one exception to the claim above, however. enter_internal() can call // exit() to drop a lock if the acquirer has been externally suspended. // In that case exit() is called with _thread_state == _thread_blocked, // but the monitor's _contentions field is > 0, which inhibits reclamation. @@ -1507,12 +1523,12 @@ void ObjectMonitor::exit(JavaThread* current, bool not_suspended) { w = entry_list_tail(current); // I'd like to write: guarantee (w->_thread != current). // But in practice an exiting thread may find itself on the entry_list. - // Let's say thread T1 calls O.wait(). Wait() enqueues T1 on O's waitset and - // then calls exit(). Exit release the lock by setting O._owner to null. - // Let's say T1 then stalls. T2 acquires O and calls O.notify(). The + // Let's say thread T1 calls O.wait(). Wait() enqueues T1 on O's waitset and + // then calls exit(). Exit releases the lock by setting O._owner to null. + // Let's say T1 then stalls. T2 acquires O and calls O.notify(). The // notify() operation moves T1 from O's waitset to O's entry_list. T2 then - // release the lock "O". T1 resumes immediately after the ST of null into - // _owner, above. T1 notices that the entry_list is populated, so it + // releases the lock "O". T1 resumes immediately after the ST of null into + // _owner, above. T1 notices that the entry_list is populated, so it // reacquires the lock and then finds itself on the entry_list. // Given all that, we have to tolerate the circumstance where "w" is // associated with current. @@ -1534,26 +1550,26 @@ void ObjectMonitor::exit(JavaThread* current, bool not_suspended) { // Normally the exiting thread is responsible for ensuring succession, // but if this thread observes other successors are ready or other // entering threads are spinning after it has stored null into _owner - // then it can exit without waking a successor. The existence of + // then it can exit without waking a successor. The existence of // spinners or ready successors guarantees proper succession (liveness). - // Responsibility passes to the ready or running successors. The exiting - // thread delegates the duty. More precisely, if a successor already + // Responsibility passes to the ready or running successors. The exiting + // thread delegates the duty. More precisely, if a successor already // exists this thread is absolved of the responsibility of waking // (unparking) one. // The _succ variable is critical to reducing futile wakeup frequency. // _succ identifies the "heir presumptive" thread that has been made - // ready (unparked) but that has not yet run. We need only one such + // ready (unparked) but that has not yet run. We need only one such // successor thread to guarantee progress. // See http://www.usenix.org/events/jvm01/full_papers/dice/dice.pdf // section 3.3 "Futile Wakeup Throttling" for details. // - // Note that spinners in Enter() also set _succ non-null. - // In the current implementation spinners opportunistically set + // Note that spinners in enter(), try_enter_fast() and enter_internal() also + // set _succ non-null. In the current implementation spinners opportunistically set // _succ so that exiting threads might avoid waking a successor. // Which means that the exiting thread could exit immediately without // waking a successor, if it observes a successor after it has dropped - // the lock. Note that the dropped lock needs to become visible to the + // the lock. Note that the dropped lock needs to become visible to the // spinner. if (_entry_list == nullptr || has_successor()) { @@ -1730,7 +1746,7 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) { if (JvmtiExport::should_post_monitor_wait()) { JvmtiExport::post_monitor_wait(current, object(), millis); } - // post monitor waited event. Note that this is past-tense, we are done waiting. + // post monitor waited event. Note that this is past-tense, we are done waiting. if (JvmtiExport::should_post_monitor_waited()) { // Note: 'false' parameter is passed here because the // wait was not timed out due to thread interrupt. @@ -1791,9 +1807,9 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) { // Enter the waiting queue, which is a circular doubly linked list in this case // but it could be a priority queue or any data structure. - // _wait_set_lock protects the wait queue. Normally the wait queue is accessed only + // _wait_set_lock protects the wait queue. Normally the wait queue is accessed only // by the owner of the monitor *except* in the case where park() - // returns because of a timeout of interrupt. Contention is exceptionally rare + // returns because of a timeout of interrupt. Contention is exceptionally rare // so we use a simple spin-lock instead of a heavier-weight blocking lock. { @@ -1850,7 +1866,7 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) { // written by the is thread. (perhaps the fetch might even be satisfied // by a look-aside into the processor's own store buffer, although given // the length of the code path between the prior ST and this load that's - // highly unlikely). If the following LD fetches a stale TS_WAIT value + // highly unlikely). If the following LD fetches a stale TS_WAIT value // then we'll acquire the lock and then re-fetch a fresh TState value. // That is, we fail toward safety. @@ -1868,7 +1884,12 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) { // No other threads will asynchronously modify TState. guarantee(node.TState != ObjectWaiter::TS_WAIT, "invariant"); OrderAccess::loadload(); - if (has_successor(current)) clear_successor(); + if (has_successor(current)) { + clear_successor(); + // Note that we do not need a fence here, as, regardless of the path taken, + // there is a fence either in ThreadBlockInVM's destructor or + // right after a call to post_monitor_wait_event(). + } // Reentry phase -- reacquire the monitor. // re-enter contended monitor after object.wait(). @@ -2046,11 +2067,11 @@ bool ObjectMonitor::notify_internal(JavaThread* current) { } } - // _wait_set_lock protects the wait queue, not the entry_list. We could + // _wait_set_lock protects the wait queue, not the entry_list. We could // move the add-to-entry_list operation, above, outside the critical section - // protected by _wait_set_lock. In practice that's not useful. With the + // protected by _wait_set_lock. In practice that's not useful. With the // exception of wait() timeouts and interrupts the monitor owner - // is the only thread that grabs _wait_set_lock. There's almost no contention + // is the only thread that grabs _wait_set_lock. There's almost no contention // on _wait_set_lock so it's not profitable to reduce the length of the // critical section. } @@ -2151,9 +2172,9 @@ void ObjectMonitor::vthread_wait(JavaThread* current, jlong millis, bool interru // Enter the waiting queue, which is a circular doubly linked list in this case // but it could be a priority queue or any data structure. - // _wait_set_lock protects the wait queue. Normally the wait queue is accessed only + // _wait_set_lock protects the wait queue. Normally the wait queue is accessed only // by the owner of the monitor *except* in the case where park() - // returns because of a timeout or interrupt. Contention is exceptionally rare + // returns because of a timeout or interrupt. Contention is exceptionally rare // so we use a simple spin-lock instead of a heavier-weight blocking lock. { @@ -2243,25 +2264,25 @@ bool ObjectMonitor::vthread_wait_reenter(JavaThread* current, ObjectWaiter* node // algorithm. // // Broadly, we can fix the spin frequency -- that is, the % of contended lock -// acquisition attempts where we opt to spin -- at 100% and vary the spin count +// acquisition attempts where we opt to spin -- at 100% and vary the spin count // (duration) or we can fix the count at approximately the duration of -// a context switch and vary the frequency. Of course we could also +// a context switch and vary the frequency. Of course we could also // vary both satisfying K == Frequency * Duration, where K is adaptive by monitor. // For a description of 'Adaptive spin-then-block mutual exclusion in // multi-threaded processing,' see U.S. Pat. No. 8046758. // // This implementation varies the duration "D", where D varies with // the success rate of recent spin attempts. (D is capped at approximately -// length of a round-trip context switch). The success rate for recent +// length of a round-trip context switch). The success rate for recent // spin attempts is a good predictor of the success rate of future spin -// attempts. The mechanism adapts automatically to varying critical +// attempts. The mechanism adapts automatically to varying critical // section length (lock modality), system load and degree of parallelism. // D is maintained per-monitor in _SpinDuration and is initialized -// optimistically. Spin frequency is fixed at 100%. +// optimistically. Spin frequency is fixed at 100%. // // Note that _SpinDuration is volatile, but we update it without locks -// or atomics. The code is designed so that _SpinDuration stays within -// a reasonable range even in the presence of races. The arithmetic +// or atomics. The code is designed so that _SpinDuration stays within +// a reasonable range even in the presence of races. The arithmetic // operations on _SpinDuration are closed over the domain of legal values, // so at worst a race will install and older but still legal value. // At the very worst this introduces some apparent non-determinism. @@ -2269,28 +2290,28 @@ bool ObjectMonitor::vthread_wait_reenter(JavaThread* current, ObjectWaiter* node // count are relatively short, even in the worst case, the effect is harmless. // // Care must be taken that a low "D" value does not become an -// an absorbing state. Transient spinning failures -- when spinning +// an absorbing state. Transient spinning failures -- when spinning // is overall profitable -- should not cause the system to converge -// on low "D" values. We want spinning to be stable and predictable +// on low "D" values. We want spinning to be stable and predictable // and fairly responsive to change and at the same time we don't want // it to oscillate, become metastable, be "too" non-deterministic, // or converge on or enter undesirable stable absorbing states. // // We implement a feedback-based control system -- using past behavior -// to predict future behavior. We face two issues: (a) if the +// to predict future behavior. We face two issues: (a) if the // input signal is random then the spin predictor won't provide optimal // results, and (b) if the signal frequency is too high then the control // system, which has some natural response lag, will "chase" the signal. -// (b) can arise from multimodal lock hold times. Transient preemption +// (b) can arise from multimodal lock hold times. Transient preemption // can also result in apparent bimodal lock hold times. // Although sub-optimal, neither condition is particularly harmful, as // in the worst-case we'll spin when we shouldn't or vice-versa. // The maximum spin duration is rather short so the failure modes aren't bad. // To be conservative, I've tuned the gain in system to bias toward -// _not spinning. Relatedly, the system can sometimes enter a mode where it -// "rings" or oscillates between spinning and not spinning. This happens +// _not spinning. Relatedly, the system can sometimes enter a mode where it +// "rings" or oscillates between spinning and not spinning. This happens // when spinning is just on the cusp of profitability, however, so the -// situation is not dire. The state is benign -- there's no need to add +// situation is not dire. The state is benign -- there's no need to add // hysteresis control to damp the transition rate between spinning and // not spinning. @@ -2322,7 +2343,9 @@ inline static int adjust_down(int spin_duration) { // Consider an AIMD scheme like: x -= (x >> 3) + 100 // This is globally sample and tends to damp the response. x -= Knob_Penalty; - if (x < 0) { x = 0; } + if (x < 0) { + x = 0; + } return x; } else { return spin_duration; @@ -2348,7 +2371,7 @@ bool ObjectMonitor::short_fixed_spin(JavaThread* current, int spin_count, bool a // Spinning: Fixed frequency (100%), vary duration bool ObjectMonitor::try_spin(JavaThread* current) { - // Dumb, brutal spin. Good for comparative measurements against adaptive spinning. + // Dumb, brutal spin. Good for comparative measurements against adaptive spinning. int knob_fixed_spin = Knob_FixedSpin; // 0 (don't spin: default), 2000 good test if (knob_fixed_spin > 0) { return short_fixed_spin(current, knob_fixed_spin, false); @@ -2357,7 +2380,7 @@ bool ObjectMonitor::try_spin(JavaThread* current) { // Admission control - verify preconditions for spinning // // We always spin a little bit, just to prevent _SpinDuration == 0 from - // becoming an absorbing state. Put another way, we spin briefly to + // becoming an absorbing state. Put another way, we spin briefly to // sample, just in case the system load, parallelism, contention, or lock // modality changed. @@ -2369,7 +2392,7 @@ bool ObjectMonitor::try_spin(JavaThread* current) { // // Consider the following alternative: // Periodically set _SpinDuration = _SpinLimit and try a long/full - // spin attempt. "Periodically" might mean after a tally of + // spin attempt. "Periodically" might mean after a tally of // the # of failed spin attempts (or iterations) reaches some threshold. // This takes us into the realm of 1-out-of-N spinning, where we // hold the duration constant but vary the frequency. @@ -2416,9 +2439,9 @@ bool ObjectMonitor::try_spin(JavaThread* current) { // If this thread observes the monitor transition or flicker // from locked to unlocked to locked, then the odds that this // thread will acquire the lock in this spin attempt go down - // considerably. The same argument applies if the CAS fails + // considerably. The same argument applies if the CAS fails // or if we observe _owner change from one non-null value to - // another non-null value. In such cases we might abort + // another non-null value. In such cases we might abort // the spin without prejudice or apply a "penalty" to the // spin count-down variable "ctr", reducing it by 100, say. @@ -2429,6 +2452,8 @@ bool ObjectMonitor::try_spin(JavaThread* current) { // The CAS succeeded -- this thread acquired ownership // Take care of some bookkeeping to exit spin state. if (has_successor(current)) { + // Note that we don't need to do OrderAccess::fence() after clearing + // _succ here, since we own the lock. clear_successor(); } @@ -2470,7 +2495,7 @@ bool ObjectMonitor::try_spin(JavaThread* current) { if (has_successor(current)) { clear_successor(); // Invariant: after setting succ=null a contending thread - // must recheck-retry _owner before parking. This usually happens + // must recheck-retry _owner before parking. This usually happens // in the normal usage of try_spin(), but it's safest // to make try_spin() as foolproof as possible. OrderAccess::fence(); diff --git a/src/hotspot/share/runtime/objectMonitor.hpp b/src/hotspot/share/runtime/objectMonitor.hpp index 842aa1b374e..3ab7b8ea519 100644 --- a/src/hotspot/share/runtime/objectMonitor.hpp +++ b/src/hotspot/share/runtime/objectMonitor.hpp @@ -89,10 +89,8 @@ class ObjectWaiter : public CHeapObj { } }; -// The ObjectMonitor class implements the heavyweight version of a -// JavaMonitor. The lightweight BasicLock/stack lock version has been -// inflated into an ObjectMonitor. This inflation is typically due to -// contention or use of Object.wait(). +// The ObjectMonitor class implements the heavyweight version of a JavaMonitor. +// This inflation is typically due to contention or use of Object.wait(). // // WARNING: This is a very sensitive and fragile class. DO NOT make any // changes unless you are fully aware of the underlying semantics. diff --git a/src/hotspot/share/runtime/objectMonitorTable.cpp b/src/hotspot/share/runtime/objectMonitorTable.cpp index bc173992d7a..9f087ad5dee 100644 --- a/src/hotspot/share/runtime/objectMonitorTable.cpp +++ b/src/hotspot/share/runtime/objectMonitorTable.cpp @@ -192,7 +192,7 @@ public: } ~Table() { - FREE_C_HEAP_ARRAY(Atomic, _buckets); + FREE_C_HEAP_ARRAY(_buckets); } Table* prev() { diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp index 129f8f76e73..f9e3a513a78 100644 --- a/src/hotspot/share/runtime/os.cpp +++ b/src/hotspot/share/runtime/os.cpp @@ -303,10 +303,10 @@ static void free_array_of_char_arrays(char** a, size_t n) { while (n > 0) { n--; if (a[n] != nullptr) { - FREE_C_HEAP_ARRAY(char, a[n]); + FREE_C_HEAP_ARRAY(a[n]); } } - FREE_C_HEAP_ARRAY(char*, a); + FREE_C_HEAP_ARRAY(a); } bool os::dll_locate_lib(char *buffer, size_t buflen, @@ -353,7 +353,7 @@ bool os::dll_locate_lib(char *buffer, size_t buflen, } } - FREE_C_HEAP_ARRAY(char*, fullfname); + FREE_C_HEAP_ARRAY(fullfname); return retval; } @@ -562,7 +562,7 @@ void* os::find_agent_function(JvmtiAgent *agent_lib, bool check_lib, const char char* agent_function_name = build_agent_function_name(sym, lib_name, agent_lib->is_absolute_path()); if (agent_function_name != nullptr) { entryName = dll_lookup(handle, agent_function_name); - FREE_C_HEAP_ARRAY(char, agent_function_name); + FREE_C_HEAP_ARRAY(agent_function_name); } return entryName; } @@ -1285,7 +1285,7 @@ void os::print_location(outputStream* st, intptr_t x, bool verbose) { bool accessible = is_readable_pointer(addr); // Check if addr points into the narrow Klass protection zone - if (UseCompressedClassPointers && CompressedKlassPointers::is_in_protection_zone(addr)) { + if (CompressedKlassPointers::is_in_protection_zone(addr)) { st->print_cr(PTR_FORMAT " points into nKlass protection zone", p2i(addr)); return; } @@ -1339,8 +1339,9 @@ void os::print_location(outputStream* st, intptr_t x, bool verbose) { } // Compressed klass needs to be decoded first. + // Todo: questionable for COH - can we do this better? #ifdef _LP64 - if (UseCompressedClassPointers && ((uintptr_t)addr &~ (uintptr_t)max_juint) == 0) { + if (((uintptr_t)addr &~ (uintptr_t)max_juint) == 0) { narrowKlass narrow_klass = (narrowKlass)(uintptr_t)addr; Klass* k = CompressedKlassPointers::decode_without_asserts(narrow_klass); @@ -1509,20 +1510,20 @@ bool os::set_boot_path(char fileSep, char pathSep) { bool has_jimage = (os::stat(jimage, &st) == 0); if (has_jimage) { Arguments::set_boot_class_path(jimage, true); - FREE_C_HEAP_ARRAY(char, jimage); + FREE_C_HEAP_ARRAY(jimage); return true; } - FREE_C_HEAP_ARRAY(char, jimage); + FREE_C_HEAP_ARRAY(jimage); // check if developer build with exploded modules char* base_classes = format_boot_path("%/modules/" JAVA_BASE_NAME, home, home_len, fileSep, pathSep); if (base_classes == nullptr) return false; if (os::stat(base_classes, &st) == 0) { Arguments::set_boot_class_path(base_classes, false); - FREE_C_HEAP_ARRAY(char, base_classes); + FREE_C_HEAP_ARRAY(base_classes); return true; } - FREE_C_HEAP_ARRAY(char, base_classes); + FREE_C_HEAP_ARRAY(base_classes); return false; } @@ -1652,7 +1653,7 @@ char** os::split_path(const char* path, size_t* elements, size_t file_name_lengt opath[i] = s; p += len + 1; } - FREE_C_HEAP_ARRAY(char, inpath); + FREE_C_HEAP_ARRAY(inpath); *elements = count; return opath; } diff --git a/src/hotspot/share/runtime/os_perf.hpp b/src/hotspot/share/runtime/os_perf.hpp index 8e39b75deb6..a9a82a41b7e 100644 --- a/src/hotspot/share/runtime/os_perf.hpp +++ b/src/hotspot/share/runtime/os_perf.hpp @@ -152,9 +152,9 @@ class SystemProcess : public CHeapObj { } virtual ~SystemProcess(void) { - FREE_C_HEAP_ARRAY(char, _name); - FREE_C_HEAP_ARRAY(char, _path); - FREE_C_HEAP_ARRAY(char, _command_line); + FREE_C_HEAP_ARRAY(_name); + FREE_C_HEAP_ARRAY(_path); + FREE_C_HEAP_ARRAY(_command_line); } }; diff --git a/src/hotspot/share/runtime/perfData.cpp b/src/hotspot/share/runtime/perfData.cpp index 7532ada8f5a..1502995203d 100644 --- a/src/hotspot/share/runtime/perfData.cpp +++ b/src/hotspot/share/runtime/perfData.cpp @@ -118,9 +118,9 @@ PerfData::PerfData(CounterNS ns, const char* name, Units u, Variability v) } PerfData::~PerfData() { - FREE_C_HEAP_ARRAY(char, _name); + FREE_C_HEAP_ARRAY(_name); if (is_on_c_heap()) { - FREE_C_HEAP_ARRAY(PerfDataEntry, _pdep); + FREE_C_HEAP_ARRAY(_pdep); } } diff --git a/src/hotspot/share/runtime/perfMemory.cpp b/src/hotspot/share/runtime/perfMemory.cpp index 9594149333e..134d1c47230 100644 --- a/src/hotspot/share/runtime/perfMemory.cpp +++ b/src/hotspot/share/runtime/perfMemory.cpp @@ -247,7 +247,7 @@ char* PerfMemory::get_perfdata_file_path() { dest_file = NEW_C_HEAP_ARRAY(char, JVM_MAXPATHLEN, mtInternal); if(!Arguments::copy_expand_pid(PerfDataSaveFile, strlen(PerfDataSaveFile), dest_file, JVM_MAXPATHLEN)) { - FREE_C_HEAP_ARRAY(char, dest_file); + FREE_C_HEAP_ARRAY(dest_file); log_debug(perf)("invalid performance data file path name specified, fall back to a default name"); } else { return dest_file; diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp index 917ff5b4545..3b5a6fa7ff8 100644 --- a/src/hotspot/share/runtime/sharedRuntime.cpp +++ b/src/hotspot/share/runtime/sharedRuntime.cpp @@ -177,6 +177,11 @@ void SharedRuntime::generate_stubs() { CAST_FROM_FN_PTR(address, SafepointSynchronize::handle_polling_page_exception)); generate_deopt_blob(); + +#if INCLUDE_CDS + // disallow any further generation of runtime stubs + AOTCodeCache::set_shared_stubs_complete(); +#endif // INCLUDE_CDS } void SharedRuntime::init_adapter_library() { @@ -3080,7 +3085,7 @@ AdapterHandlerEntry::~AdapterHandlerEntry() { _fingerprint = nullptr; } #ifdef ASSERT - FREE_C_HEAP_ARRAY(unsigned char, _saved_code); + FREE_C_HEAP_ARRAY(_saved_code); #endif FreeHeap(this); } @@ -3371,7 +3376,7 @@ JRT_LEAF(intptr_t*, SharedRuntime::OSR_migration_begin( JavaThread *current) ) JRT_END JRT_LEAF(void, SharedRuntime::OSR_migration_end( intptr_t* buf) ) - FREE_C_HEAP_ARRAY(intptr_t, buf); + FREE_C_HEAP_ARRAY(buf); JRT_END const char* AdapterHandlerLibrary::name(AdapterHandlerEntry* handler) { diff --git a/src/hotspot/share/runtime/stubCodeGenerator.cpp b/src/hotspot/share/runtime/stubCodeGenerator.cpp index 43250c004ca..45e40f4a754 100644 --- a/src/hotspot/share/runtime/stubCodeGenerator.cpp +++ b/src/hotspot/share/runtime/stubCodeGenerator.cpp @@ -23,6 +23,7 @@ */ #include "asm/macroAssembler.inline.hpp" +#include "code/aotCodeCache.hpp" #include "code/codeCache.hpp" #include "compiler/disassembler.hpp" #include "oops/oop.inline.hpp" @@ -30,7 +31,9 @@ #include "prims/jvmtiExport.hpp" #include "runtime/stubCodeGenerator.hpp" #include "runtime/stubRoutines.hpp" - +#if INCLUDE_ZGC +#include "gc/z/zBarrierSetAssembler.hpp" +#endif // INCLUDE_ZGC // Implementation of StubCodeDesc @@ -69,14 +72,16 @@ void StubCodeDesc::print() const { print_on(tty); } StubCodeGenerator::StubCodeGenerator(CodeBuffer* code, bool print_code) { _masm = new MacroAssembler(code); _blob_id = BlobId::NO_BLOBID; + _stub_data = nullptr; _print_code = PrintStubCode || print_code; } -StubCodeGenerator::StubCodeGenerator(CodeBuffer* code, BlobId blob_id, bool print_code) { +StubCodeGenerator::StubCodeGenerator(CodeBuffer* code, BlobId blob_id, AOTStubData* stub_data, bool print_code) { assert(StubInfo::is_stubgen(blob_id), "not a stubgen blob %s", StubInfo::name(blob_id)); _masm = new MacroAssembler(code); _blob_id = blob_id; + _stub_data = stub_data; _print_code = PrintStubCode || print_code; } @@ -91,11 +96,92 @@ StubCodeGenerator::~StubCodeGenerator() { #endif } +void StubCodeGenerator::setup_code_desc(const char* name, address start, address end, bool loaded_from_cache) { + StubCodeDesc* cdesc = new StubCodeDesc("StubRoutines", name, start, end); + cdesc->set_disp(uint(start - _masm->code_section()->outer()->insts_begin())); + if (loaded_from_cache) { + cdesc->set_loaded_from_cache(); + } + print_stub_code_desc(cdesc); + // copied from ~StubCodeMark() + Forte::register_stub(cdesc->name(), cdesc->begin(), cdesc->end()); + if (JvmtiExport::should_post_dynamic_code_generated()) { + JvmtiExport::post_dynamic_code_generated(cdesc->name(), cdesc->begin(), cdesc->end()); + } +} + +// Helper used to restore ranges and handler addresses restored from +// AOT cache. Expects entries to contain 3 * count addresses beginning +// at offset begin which identify start of range, end of range and +// address of handler pc. start and end of range may not be null. +// handler pc may be null in which case it defaults to the +// default_handler. + +void StubCodeGenerator::register_unsafe_access_handlers(GrowableArray
&entries, int begin, int count) { + for (int i = 0; i < count; i++) { + int offset = begin + 3 * i; + address start = entries.at(offset); + address end = entries.at(offset + 1); + address handler = entries.at(offset + 2); + assert(start != nullptr, "sanity"); + assert(end != nullptr, "sanity"); + if (handler == nullptr) { + assert(UnsafeMemoryAccess::common_exit_stub_pc() != nullptr, + "default unsafe handler must be set before registering unsafe rgeionwiht no handler!"); + handler = UnsafeMemoryAccess::common_exit_stub_pc(); + } + UnsafeMemoryAccess::add_to_table(start, end, handler); + } +} + +// Helper used to retrieve ranges and handler addresses registered +// during generation of the stub which spans [start, end) in order to +// allow them to be saved to an AOT cache. +void StubCodeGenerator::retrieve_unsafe_access_handlers(address start, address end, GrowableArray
&entries) { + UnsafeMemoryAccess::collect_entries(start, end, entries); +} + +#if INCLUDE_ZGC +// Helper used to restore ZGC pointer colouring relocation addresses +// retrieved from the AOT cache. +void StubCodeGenerator::register_reloc_addresses(GrowableArray
&entries, int begin, int count) { + LogTarget(Trace, aot, codecache, stubs) lt; + if (lt.is_enabled()) { + LogStream ls(lt); + for (int i = begin; i < count; i++) { + ls.print_cr("Registered reloc address " INTPTR_FORMAT, p2i(entries.at(i))); + } + } + ZBarrierSetAssembler *zbs = (ZBarrierSetAssembler*)BarrierSet::barrier_set()->barrier_set_assembler(); + zbs->register_reloc_addresses(entries, begin, count); +} + +// Helper used to retrieve ranges and handler addresses registered +// during generation of the stub which spans [start, end) in order to +// allow them to be saved to an AOT cache. +void StubCodeGenerator::retrieve_reloc_addresses(address start, address end, GrowableArray
&entries) { + int l = entries.length(); + ZBarrierSetAssembler *zbs = (ZBarrierSetAssembler*)BarrierSet::barrier_set()->barrier_set_assembler(); + zbs->retrieve_reloc_addresses(start, end, entries); + LogTarget(Trace, aot, codecache, stubs) lt; + if (lt.is_enabled()) { + LogStream ls(lt); + for (int i = l; i < entries.length(); i++) { + ls.print_cr("retrieved reloc address " INTPTR_FORMAT, p2i(entries.at(i))); + } + } +} +#endif // INCLUDE_ZGC + void StubCodeGenerator::stub_prolog(StubCodeDesc* cdesc) { // default implementation - do nothing } void StubCodeGenerator::stub_epilog(StubCodeDesc* cdesc) { + print_stub_code_desc(cdesc); +} + +void StubCodeGenerator::print_stub_code_desc(StubCodeDesc* cdesc) { LogTarget(Debug, stubs) lt; if (lt.is_enabled()) { LogStream ls(lt); @@ -119,6 +205,52 @@ void StubCodeGenerator::stub_epilog(StubCodeDesc* cdesc) { } } +address StubCodeGenerator::load_archive_data(StubId stub_id, GrowableArray
*entries, GrowableArray
* extras) { + // punt to stub data if it exists and is not for dumping + if (_stub_data == nullptr || _stub_data->is_dumping()) { + return nullptr; + } + // punt to stub data + address start, end; + start = _stub_data->load_archive_data(stub_id, end, entries, extras); + + if (start != nullptr) { + setup_code_desc(StubInfo::name(stub_id), start, end, true); + } + + return start; +} + +void StubCodeGenerator::store_archive_data(StubId stub_id, address start, address end, GrowableArray
* entries, GrowableArray
* extras) { + // punt to stub data if we have any + if (_stub_data != nullptr) { + _stub_data->store_archive_data(stub_id, start, end, entries, extras); + } +} + +void StubCodeGenerator::print_statistics_on(outputStream* st) { + st->print_cr("StubRoutines Stubs:"); + st->print_cr(" Initial stubs: %d", StubInfo::stub_count(BlobId::stubgen_initial_id)); + st->print_cr(" Continuation stubs: %d", StubInfo::stub_count(BlobId::stubgen_continuation_id)); + st->print_cr(" Compiler stubs: %d", StubInfo::stub_count(BlobId::stubgen_compiler_id)); + st->print_cr(" Final stubs: %d", StubInfo::stub_count(BlobId::stubgen_final_id)); + + int emitted = 0; + int loaded_from_cache = 0; + + StubCodeDesc* scd = StubCodeDesc::first(); + while (scd != nullptr) { + if (!strcmp(scd->group(), "StubRoutines")) { + emitted += 1; + if (scd->loaded_from_cache()) { + loaded_from_cache += 1; + } + } + scd = StubCodeDesc::next(scd); + } + st->print_cr("Total stubroutines stubs emitted: %d (generated=%d, loaded from cache=%d)", emitted, emitted - loaded_from_cache, loaded_from_cache); +} + #ifdef ASSERT void StubCodeGenerator::verify_stub(StubId stub_id) { assert(StubRoutines::stub_to_blob(stub_id) == blob_id(), "wrong blob %s for generation of stub %s", StubRoutines::get_blob_name(blob_id()), StubRoutines::get_stub_name(stub_id)); diff --git a/src/hotspot/share/runtime/stubCodeGenerator.hpp b/src/hotspot/share/runtime/stubCodeGenerator.hpp index 7d8944c85ea..958fa76543b 100644 --- a/src/hotspot/share/runtime/stubCodeGenerator.hpp +++ b/src/hotspot/share/runtime/stubCodeGenerator.hpp @@ -26,6 +26,7 @@ #define SHARE_RUNTIME_STUBCODEGENERATOR_HPP #include "asm/assembler.hpp" +#include "code/aotCodeCache.hpp" #include "memory/allocation.hpp" #include "runtime/stubInfo.hpp" @@ -48,6 +49,7 @@ class StubCodeDesc: public CHeapObj { address _begin; // points to the first byte of the stub code (included) address _end; // points to the first byte after the stub code (excluded) uint _disp; // Displacement relative base address in buffer. + bool _loaded_from_cache; friend class StubCodeMark; friend class StubCodeGenerator; @@ -65,6 +67,8 @@ class StubCodeDesc: public CHeapObj { void set_disp(uint disp) { _disp = disp; } + void set_loaded_from_cache() { _loaded_from_cache = true; } + public: static StubCodeDesc* first() { return _list; } static StubCodeDesc* next(StubCodeDesc* desc) { return desc->_next; } @@ -81,6 +85,7 @@ class StubCodeDesc: public CHeapObj { _end = end; _disp = 0; _list = this; + _loaded_from_cache = false; }; static void freeze(); @@ -93,12 +98,11 @@ class StubCodeDesc: public CHeapObj { uint disp() const { return _disp; } int size_in_bytes() const { return pointer_delta_as_int(_end, _begin); } bool contains(address pc) const { return _begin <= pc && pc < _end; } + bool loaded_from_cache() const { return _loaded_from_cache; } void print_on(outputStream* st) const; void print() const; }; -// forward declare blob and stub id enums - // The base class for all stub-generating code generators. // Provides utility functions. @@ -108,10 +112,20 @@ class StubCodeGenerator: public StackObj { BlobId _blob_id; protected: MacroAssembler* _masm; + AOTStubData* _stub_data; - public: + void setup_code_desc(const char* name, address start, address end, bool loaded_from_cache); + // unsafe handler management + void register_unsafe_access_handlers(GrowableArray
&entries, int begin, int count); + void retrieve_unsafe_access_handlers(address start, address end, GrowableArray
&entries); +#if INCLUDE_ZGC + void register_reloc_addresses(GrowableArray
&entries, int begin, int count); + void retrieve_reloc_addresses(address start, address end, GrowableArray
&entries); +#endif // INCLUDE_ZGC + +public: StubCodeGenerator(CodeBuffer* code, bool print_code = false); - StubCodeGenerator(CodeBuffer* code, BlobId blob_id, bool print_code = false); + StubCodeGenerator(CodeBuffer* code, BlobId blob_id, AOTStubData* stub_data = nullptr, bool print_code = false); ~StubCodeGenerator(); MacroAssembler* assembler() const { return _masm; } @@ -120,9 +134,59 @@ class StubCodeGenerator: public StackObj { virtual void stub_prolog(StubCodeDesc* cdesc); // called by StubCodeMark constructor virtual void stub_epilog(StubCodeDesc* cdesc); // called by StubCodeMark destructor + void print_stub_code_desc(StubCodeDesc* cdesc); + + static void print_statistics_on(outputStream* st); + + // load_archive_data should be called before generating the stub + // identified by stub_id. If AOT caching of stubs is enabled and the + // stubis found then the address of the stub's first and, possibly, + // only entry is returned and the caller should use it instead of + // generating thestub. Otherwise a null address is returned and the + // caller should proceed to generate the stub. + // + // store_archive_data should be called when a stub has been + // successfully generated into the current blob irrespctive of + // whether the current JVM is generating or consuming an AOT archive + // (the caller should not check for either case). When generating an + // archive the stub entry and end addresses are recorded for storage + // along with the current blob and also to allow rences to the stub + // from other stubs or from compiled Java methods can be detected + // and marked as requiring relocation. When consuming an archive the + // stub entry address is still inorer to identify it as a relocation + // target. When no archive is in use the call has no side effects. + // + // start and end identify the inclusive start and exclusive end + // address for stub code and must lie in the current blob's code + // range. Stubs presented via this interface must declare at least + // one entry and start is always taken to be the first entry. + // + // Optional arrays entries and extras store other addresses of + // interest all of which must either lie in the interval (start, + // end) or be nullptr (verified by load and store methods). + // + // entries lists secondary entries for the stub each of which must + // match a corresponding entry declaration for the stub (entry count + // verified by load and store methods). Null entry addresses are + // allowed when an architecture does not require a specific entry + // but may not vary from one run to the next. If the cache is in use + // at a store (for loading or saving code) then non-null entry + // addresses are entered into the AOT cache stub address table + // allowing references to them from other stubs or nmethods to be + // relocated. + // + // extras lists other non-entry stub addresses of interest such as + // memory protection ranges and associated handler addresses + // (potentially including a null address). These do do not need to + // be declared as entries and their number and meaning may vary + // according to the architecture. + + address load_archive_data(StubId stub_id, GrowableArray
*entries = nullptr, GrowableArray
* extras = nullptr); + void store_archive_data(StubId stub_id, address start, address end, GrowableArray
*entries = nullptr, GrowableArray
* extras = nullptr); #ifdef ASSERT void verify_stub(StubId stub_id); #endif + }; // Stack-allocated helper class used to associate a stub code with a name. diff --git a/src/hotspot/share/runtime/stubDeclarations.hpp b/src/hotspot/share/runtime/stubDeclarations.hpp index c478eda3e7c..ed1b3ea2e78 100644 --- a/src/hotspot/share/runtime/stubDeclarations.hpp +++ b/src/hotspot/share/runtime/stubDeclarations.hpp @@ -539,18 +539,19 @@ // generated. // // Architecture-specific entries need to be declared using the -// do_arch_entry template +// do_arch_entry templates // // do_arch_entry(arch, blob_name, stub_name, field_name, getter_name) // // do_arch_entry_init(arch, blob_name, stub_name, field_name, // getter_name, init_function) // +// do_arch_entry_array(arch, blob_name, stub_name, field_name, +// getter_name, count) +// // The only difference between these templates and the generic ones is // that they receive an extra argument which identifies the current // architecture e.g. x86, aarch64 etc. -// -// Currently there is no support for a do_arch_array_entry template. // Include arch-specific stub and entry declarations and make sure the // relevant template macros have been defined @@ -598,7 +599,8 @@ do_entry, do_entry_init, \ do_entry_array, \ do_arch_blob, \ - do_arch_entry, do_arch_entry_init) \ + do_arch_entry, do_arch_entry_init, \ + do_arch_entry_array) \ do_blob(preuniverse) \ do_stub(preuniverse, fence) \ do_entry(preuniverse, fence, fence_entry, fence_entry) \ @@ -615,7 +617,8 @@ atomic_cmpxchg_long_entry) \ /* merge in stubs and entries declared in arch header */ \ STUBGEN_PREUNIVERSE_BLOBS_ARCH_DO(do_stub, do_arch_blob, \ - do_arch_entry, do_arch_entry_init) \ + do_arch_entry, do_arch_entry_init, \ + do_arch_entry_array) \ end_blob(preuniverse) \ #define STUBGEN_INITIAL_BLOBS_DO(do_blob, end_blob, \ @@ -623,7 +626,8 @@ do_entry, do_entry_init, \ do_entry_array, \ do_arch_blob, \ - do_arch_entry, do_arch_entry_init) \ + do_arch_entry, do_arch_entry_init, \ + do_arch_entry_array) \ do_blob(initial) \ do_stub(initial, call_stub) \ do_entry(initial, call_stub, call_stub_entry, call_stub_entry) \ @@ -667,19 +671,10 @@ do_entry(initial, dcbrt, dcbrt, dcbrt) \ do_stub(initial, fmod) \ do_entry(initial, fmod, fmod, fmod) \ - /* following generic entries should really be x86_32 only */ \ - do_stub(initial, dlibm_sin_cos_huge) \ - do_entry(initial, dlibm_sin_cos_huge, dlibm_sin_cos_huge, \ - dlibm_sin_cos_huge) \ - do_stub(initial, dlibm_reduce_pi04l) \ - do_entry(initial, dlibm_reduce_pi04l, dlibm_reduce_pi04l, \ - dlibm_reduce_pi04l) \ - do_stub(initial, dlibm_tan_cot_huge) \ - do_entry(initial, dlibm_tan_cot_huge, dlibm_tan_cot_huge, \ - dlibm_tan_cot_huge) \ /* merge in stubs and entries declared in arch header */ \ STUBGEN_INITIAL_BLOBS_ARCH_DO(do_stub, do_arch_blob, \ - do_arch_entry, do_arch_entry_init) \ + do_arch_entry, do_arch_entry_init, \ + do_arch_entry_array) \ end_blob(initial) \ @@ -689,7 +684,8 @@ do_entry_array, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_blob(continuation) \ do_stub(continuation, cont_thaw) \ do_entry(continuation, cont_thaw, cont_thaw, cont_thaw) \ @@ -704,7 +700,8 @@ cont_returnBarrierExc) \ /* merge in stubs and entries declared in arch header */ \ STUBGEN_CONTINUATION_BLOBS_ARCH_DO(do_stub, do_arch_blob, \ - do_arch_entry, do_arch_entry_init) \ + do_arch_entry, do_arch_entry_init, \ + do_arch_entry_array) \ end_blob(continuation) \ @@ -713,7 +710,8 @@ do_entry, do_entry_init, \ do_entry_array, \ do_arch_blob, \ - do_arch_entry, do_arch_entry_init) \ + do_arch_entry, do_arch_entry_init, \ + do_arch_entry_array) \ do_blob(compiler) \ do_stub(compiler, array_sort) \ do_entry(compiler, array_sort, array_sort, select_arraysort_function) \ @@ -858,7 +856,8 @@ bigIntegerLeftShiftWorker, bigIntegerLeftShift) \ /* merge in stubs and entries declared in arch header */ \ STUBGEN_COMPILER_BLOBS_ARCH_DO(do_stub, do_arch_blob, \ - do_arch_entry, do_arch_entry_init) \ + do_arch_entry, do_arch_entry_init, \ + do_arch_entry_array) \ end_blob(compiler) \ @@ -867,7 +866,8 @@ do_entry, do_entry_init, \ do_entry_array, \ do_arch_blob, \ - do_arch_entry, do_arch_entry_init) \ + do_arch_entry, do_arch_entry_init, \ + do_arch_entry_array) \ do_blob(final) \ do_stub(final, verify_oop) \ do_entry(final, verify_oop, verify_oop_subroutine_entry, \ @@ -962,9 +962,15 @@ do_entry_init(final, arrayof_jlong_arraycopy, \ arrayof_jlong_arraycopy, arrayof_jlong_arraycopy, \ StubRoutines::arrayof_jlong_copy) \ + do_entry(final, arrayof_jlong_arraycopy, \ + arrayof_jlong_arraycopy_nopush, \ + arrayof_jlong_arraycopy_nopush) \ do_stub(final, arrayof_oop_arraycopy) \ do_entry_init(final, arrayof_oop_arraycopy, arrayof_oop_arraycopy, \ arrayof_oop_arraycopy, StubRoutines::arrayof_oop_copy) \ + do_entry(final, arrayof_oop_arraycopy, \ + arrayof_oop_arraycopy_nopush, \ + arrayof_oop_arraycopy_nopush) \ do_stub(final, arrayof_oop_arraycopy_uninit) \ do_entry_init(final, arrayof_oop_arraycopy_uninit, \ arrayof_oop_arraycopy_uninit, \ @@ -1073,7 +1079,8 @@ lookup_secondary_supers_table_slow_path_stub) \ /* merge in stubs and entries declared in arch header */ \ STUBGEN_FINAL_BLOBS_ARCH_DO(do_stub, do_arch_blob, \ - do_arch_entry, do_arch_entry_init) \ + do_arch_entry, do_arch_entry_init, \ + do_arch_entry_array) \ end_blob(final) \ @@ -1086,37 +1093,43 @@ do_entry, do_entry_init, \ do_entry_array, \ do_arch_blob, \ - do_arch_entry, do_arch_entry_init) \ + do_arch_entry, do_arch_entry_init, \ + do_arch_entry_array) \ STUBGEN_PREUNIVERSE_BLOBS_DO(do_blob, end_blob, \ do_stub, \ do_entry, do_entry_init, \ do_entry_array, \ do_arch_blob, \ - do_arch_entry, do_arch_entry_init) \ + do_arch_entry, do_arch_entry_init, \ + do_arch_entry_array) \ STUBGEN_INITIAL_BLOBS_DO(do_blob, end_blob, \ do_stub, \ do_entry, do_entry_init, \ do_entry_array, \ do_arch_blob, \ - do_arch_entry, do_arch_entry_init) \ + do_arch_entry, do_arch_entry_init, \ + do_arch_entry_array) \ STUBGEN_CONTINUATION_BLOBS_DO(do_blob, end_blob, \ do_stub, \ do_entry, do_entry_init, \ do_entry_array, \ do_arch_blob, \ - do_arch_entry, do_arch_entry_init) \ + do_arch_entry, do_arch_entry_init, \ + do_arch_entry_array) \ STUBGEN_COMPILER_BLOBS_DO(do_blob, end_blob, \ do_stub, \ do_entry, do_entry_init, \ do_entry_array, \ do_arch_blob, \ - do_arch_entry, do_arch_entry_init) \ + do_arch_entry, do_arch_entry_init, \ + do_arch_entry_array) \ STUBGEN_FINAL_BLOBS_DO(do_blob, end_blob, \ do_stub, \ do_entry, do_entry_init, \ do_entry_array, \ do_arch_blob, \ - do_arch_entry, do_arch_entry_init) \ + do_arch_entry, do_arch_entry_init, \ + do_arch_entry_array) \ // Convenience macros for use by template implementations @@ -1166,6 +1179,9 @@ #define STUBGEN_COUNT5(_1, _2, _3, _4, count) \ + count +#define STUBGEN_COUNT6(_1, _2, _3, _4, _5, count) \ + + count + // Convenience templates that emit nothing // ignore do_blob(blob_name, type) declarations @@ -1204,7 +1220,8 @@ DO_ENTRY_EMPTY4, DO_ENTRY_EMPTY5, \ DO_ENTRY_EMPTY5, \ DO_ARCH_BLOB_EMPTY2, \ - DO_ARCH_ENTRY_EMPTY5, DO_ARCH_ENTRY_EMPTY6) \ + DO_ARCH_ENTRY_EMPTY5, DO_ARCH_ENTRY_EMPTY6, \ + DO_ARCH_ENTRY_EMPTY6) \ // client macro to operate only on StubGenerator stubs @@ -1214,7 +1231,8 @@ DO_ENTRY_EMPTY4, DO_ENTRY_EMPTY5, \ DO_ENTRY_EMPTY5, \ DO_ARCH_BLOB_EMPTY2, \ - DO_ARCH_ENTRY_EMPTY5, DO_ARCH_ENTRY_EMPTY6) \ + DO_ARCH_ENTRY_EMPTY5, DO_ARCH_ENTRY_EMPTY6, \ + DO_ARCH_ENTRY_EMPTY6) \ // client macros to operate only on StubGenerator blobs and stubs @@ -1224,18 +1242,21 @@ DO_ENTRY_EMPTY4, DO_ENTRY_EMPTY5, \ DO_ENTRY_EMPTY5, \ DO_ARCH_BLOB_EMPTY2, \ - DO_ARCH_ENTRY_EMPTY5,DO_ARCH_ENTRY_EMPTY6) \ + DO_ARCH_ENTRY_EMPTY5,DO_ARCH_ENTRY_EMPTY6, \ + DO_ARCH_ENTRY_EMPTY6) \ // client macro to operate only on StubGenerator generci and arch entries #define STUBGEN_ALL_ENTRIES_DO(do_entry, do_entry_init, do_entry_array, \ - do_arch_entry, do_arch_entry_init) \ + do_arch_entry, do_arch_entry_init, \ + do_arch_entry_array) \ STUBGEN_ALL_DO(DO_BLOB_EMPTY1, DO_BLOB_EMPTY1, \ DO_STUB_EMPTY2, \ do_entry, do_entry_init, \ do_entry_array, \ DO_ARCH_BLOB_EMPTY2, \ - do_arch_entry, do_arch_entry_init) \ + do_arch_entry, do_arch_entry_init, \ + do_arch_entry_array) \ // client macro to operate only on StubGenerator entries @@ -1245,7 +1266,8 @@ do_entry, do_entry_init, \ do_entry_array, \ DO_ARCH_BLOB_EMPTY2, \ - DO_ARCH_ENTRY_EMPTY5, DO_ARCH_ENTRY_EMPTY6) \ + DO_ARCH_ENTRY_EMPTY5, DO_ARCH_ENTRY_EMPTY6, \ + DO_ARCH_ENTRY_EMPTY6) \ // client macro to operate only on StubGenerator arch blobs @@ -1255,16 +1277,19 @@ DO_ENTRY_EMPTY4, DO_ENTRY_EMPTY5, \ DO_ENTRY_EMPTY5, \ do_arch_blob, \ - DO_ARCH_ENTRY_EMPTY5, DO_ARCH_ENTRY_EMPTY6) \ + DO_ARCH_ENTRY_EMPTY5, DO_ARCH_ENTRY_EMPTY6, \ + DO_ARCH_ENTRY_EMPTY6) \ // client macro to operate only on StubGenerator arch entries -#define STUBGEN_ARCH_ENTRIES_DO(do_arch_entry, do_arch_entry_init) \ +#define STUBGEN_ARCH_ENTRIES_DO(do_arch_entry, do_arch_entry_init, \ + do_arch_entry_array) \ STUBGEN_ALL_DO(DO_BLOB_EMPTY1, DO_BLOB_EMPTY1, \ DO_STUB_EMPTY2, \ DO_ENTRY_EMPTY4, DO_ENTRY_EMPTY5, \ DO_ENTRY_EMPTY5, \ DO_ARCH_BLOB_EMPTY2, \ - do_arch_entry, do_arch_entry_init) \ + do_arch_entry, do_arch_entry_init, \ + do_arch_entry_array) \ #endif // SHARE_RUNTIME_STUBDECLARATIONS_HPP diff --git a/src/hotspot/share/runtime/stubInfo.cpp b/src/hotspot/share/runtime/stubInfo.cpp index ee90631145a..4d4d865cf95 100644 --- a/src/hotspot/share/runtime/stubInfo.cpp +++ b/src/hotspot/share/runtime/stubInfo.cpp @@ -574,6 +574,18 @@ void StubInfo::process_stubgen_entry(StubGroup& group_cursor, field_name, id), \ 0); \ +#define PROCESS_STUBGEN_ENTRY_ARCH_ARRAY(arch_name, blob, stub, \ + field_name, getter_name, \ + count) \ + process_stubgen_entry(_group_cursor, _blob_cursor, \ + _stub_cursor, _entry_cursor, \ + #arch_name "_" # field_name "_entry (stub gen)", \ + BlobId:: JOIN3(stubgen, blob, id), \ + StubId:: JOIN3(stubgen, stub, id), \ + EntryId:: JOIN4(stubgen, arch_name, \ + field_name, id), \ + count); \ + void StubInfo::populate_stub_tables() { StubGroup _group_cursor; BlobId _blob_cursor = BlobId::NO_BLOBID; @@ -615,7 +627,8 @@ void StubInfo::populate_stub_tables() { PROCESS_STUBGEN_ENTRY, PROCESS_STUBGEN_ENTRY_INIT, PROCESS_STUBGEN_ENTRY_ARRAY, DO_ARCH_BLOB_EMPTY2, - PROCESS_STUBGEN_ENTRY_ARCH, PROCESS_STUBGEN_ENTRY_ARCH_INIT); + PROCESS_STUBGEN_ENTRY_ARCH, PROCESS_STUBGEN_ENTRY_ARCH_INIT, + PROCESS_STUBGEN_ENTRY_ARCH_ARRAY); assert(next(_blob_cursor) == BlobId::NUM_BLOBIDS, "should have exhausted all blob ids!"); assert(next(_stub_cursor) == StubId::NUM_STUBIDS, "should have exhausted all stub ids!"); assert(next(_entry_cursor) == EntryId::NUM_ENTRYIDS, "should have exhausted all entry ids!"); @@ -636,6 +649,7 @@ void StubInfo::populate_stub_tables() { #undef PROCESS_STUBGEN_ENTRY_ARRAY #undef PROCESS_STUBGEN_ENTRY_ARCH #undef PROCESS_STUBGEN_ENTRY_ARCH_INIT +#undef PROCESS_STUBGEN_ENTRY_ARCH_ARRAY #ifdef ASSERT @@ -1087,6 +1101,15 @@ int StubInfo::stubgen_offset(StubId id) { return local_offset(StubGroup::STUBGEN, id); } +int StubInfo::stubgen_offset_in_blob(BlobId blob_id, StubId id) { + assert(blob(id) == blob_id, "sanity!"); + StubGroup group = StubGroup::STUBGEN; + assert(stubgroup(blob_id) == group, "sanity"); + StubId base_id = stub_base(blob_id); + assert(base_id != StubId::NO_STUBID, "sanity"); + return local_offset(group, id) - local_offset(group, base_id); +} + // initialization function called to populate blob. stub and entry // tables. this must be called before any stubs are generated void initialize_stub_info() { diff --git a/src/hotspot/share/runtime/stubInfo.hpp b/src/hotspot/share/runtime/stubInfo.hpp index 9ed6e0cb9f9..2fe503a8d0e 100644 --- a/src/hotspot/share/runtime/stubInfo.hpp +++ b/src/hotspot/share/runtime/stubInfo.hpp @@ -349,6 +349,14 @@ enum class StubId : int { init_function) \ JOIN4(stubgen, arch_name, field_name, id), \ +#define STUBGEN_DECLARE_ARCH_ARRAY_TAG(arch_name, blob_name, stub_name, \ + field_name, getter_name, \ + count) \ + JOIN4(stubgen, arch_name, field_name, id), \ + JOIN4(stubgen, arch_name, field_name, max) = \ + JOIN4(stubgen, arch_name, field_name, id) + \ + count - 1, \ + // the above macros are enough to declare the enum enum class EntryId : int { @@ -366,7 +374,8 @@ enum class EntryId : int { STUBGEN_DECLARE_INIT_TAG, STUBGEN_DECLARE_ARRAY_TAG, STUBGEN_DECLARE_ARCH_TAG, - STUBGEN_DECLARE_ARCH_INIT_TAG) + STUBGEN_DECLARE_ARCH_INIT_TAG, + STUBGEN_DECLARE_ARCH_ARRAY_TAG) NUM_ENTRYIDS }; @@ -379,6 +388,7 @@ enum class EntryId : int { #undef STUBGEN_DECLARE_ARRAY_TAG #undef STUBGEN_DECLARE_ARCH_TAG #undef STUBGEN_DECLARE_ARCH_INIT_TAG +#undef STUBGEN_DECLARE_ARCH_ARRAY_TAG // we need static init expressions for blob, stub and entry counts in // each stubgroup @@ -404,7 +414,8 @@ enum class EntryId : int { #define STUBGEN_ENTRY_COUNT_INITIALIZER \ 0 STUBGEN_ALL_ENTRIES_DO(COUNT4, COUNT5, \ STUBGEN_COUNT5, \ - COUNT5, COUNT6) + COUNT5, COUNT6, \ + STUBGEN_COUNT6) // Declare management class StubInfo @@ -669,6 +680,11 @@ public: static int c1_offset(StubId id); static int c2_offset(StubId id); static int stubgen_offset(StubId id); + + // Convert a stub id to a unique, zero-based offset in the range of + // stub ids for a given blob in the stubgen stub group. + + static int stubgen_offset_in_blob(BlobId blob_id, StubId id); }; diff --git a/src/hotspot/share/runtime/stubRoutines.cpp b/src/hotspot/share/runtime/stubRoutines.cpp index 5246613738e..f5509b9d996 100644 --- a/src/hotspot/share/runtime/stubRoutines.cpp +++ b/src/hotspot/share/runtime/stubRoutines.cpp @@ -102,8 +102,7 @@ BlobId StubRoutines::stub_to_blob(StubId id) { // Initialization -extern void StubGenerator_generate(CodeBuffer* code, BlobId blob_id); // only interface to generators - +extern void StubGenerator_generate(CodeBuffer* code, BlobId blob_id, AOTStubData* stub_data); // only interface to generators void UnsafeMemoryAccess::create_table(int max_size) { UnsafeMemoryAccess::_table = new UnsafeMemoryAccess[max_size]; UnsafeMemoryAccess::_table_max_length = max_size; @@ -154,7 +153,8 @@ void UnsafeMemoryAccess::collect_entries(address range_start, address range_end, if (e._error_exit_pc != _common_exit_stub_pc) { entries.append(e._error_exit_pc); } else { - // an address outside the stub must be the common exit stub address + // an address outside the stub must be the common exit stub + // address which is marked with a null address entries.append(nullptr); } } @@ -169,6 +169,38 @@ static BufferBlob* initialize_stubs(BlobId blob_id, assert(StubInfo::is_stubgen(blob_id), "not a stubgen blob %s", StubInfo::name(blob_id)); ResourceMark rm; TraceTime timer(timer_msg, TRACETIME_LOG(Info, startuptime)); + // If we are loading stubs we need to check if we can retrieve a + // blob and/or an associated archived stub descriptor from the + // AOTCodeCache. If we are storing stubs we need to create a blob + // but we still need a stub data descriptor to fill in during + // generation. + AOTStubData stub_data(blob_id); + AOTStubData* stub_data_p = nullptr; + LogTarget(Info, stubs) lt; + + // we need to track and publish details of stubs in a stubgen blob + // when we are 1) using stubs from the cache 2) dumping stubs to the + // cache 3) generating stubs that may be needed by other cache + // elements. + + if (stub_data.is_open()) { + stub_data_p = &stub_data; + } + if (code_size > 0 && stub_data.is_using()) { + // try to load the blob and details of its stubs from cache. if + // that fails we will still generate all necessary stubs + if (stub_data.load_code_blob()) { + if (lt.is_enabled()) { + LogStream ls(lt); + ls.print_cr("Found blob %s in AOT cache", StubInfo::name(blob_id)); + } + } + } + + // Even if we managed to load a blob from the AOT cache we still + // need to allocate a code blob and associated buffer. The AOT blob + // may not include all the stubs we need for this runtime. + // Add extra space for large CodeEntryAlignment int size = code_size + CodeEntryAlignment * max_aligned_stubs; BufferBlob* stubs_code = BufferBlob::create(buffer_name, size); @@ -178,6 +210,10 @@ static BufferBlob* initialize_stubs(BlobId blob_id, // In that case we can tolerate an allocation failure because the // compiler will have been shut down and we have no need of the // blob. + // TODO: Ideally we would still like to try to use any AOT cached + // blob here but we don't have a fallback if we find that it is + // missing stubs we need so for now we exit. This should only + // happen in cases where we have a very small code cache. if (Thread::current()->is_Compiler_thread()) { assert(blob_id == BlobId::stubgen_compiler_id, "sanity"); assert(DelayCompilerStubsGeneration, "sanity"); @@ -187,10 +223,12 @@ static BufferBlob* initialize_stubs(BlobId blob_id, vm_exit_out_of_memory(code_size, OOM_MALLOC_ERROR, "CodeCache: no room for %s", buffer_name); } CodeBuffer buffer(stubs_code); - StubGenerator_generate(&buffer, blob_id); + short buffer_locs[20]; + buffer.insts()->initialize_shared_locs((relocInfo*)buffer_locs, + sizeof(buffer_locs)/sizeof(relocInfo)); + StubGenerator_generate(&buffer, blob_id, stub_data_p); if (code_size == 0) { assert(buffer.insts_size() == 0, "should not write into buffer when bob size declared as 0"); - LogTarget(Info, stubs) lt; if (lt.is_enabled()) { LogStream ls(lt); ls.print_cr("%s\t not generated", buffer_name); @@ -203,7 +241,37 @@ static BufferBlob* initialize_stubs(BlobId blob_id, "increase %s, code_size: %d, used: %d, free: %d", assert_msg, code_size, buffer.total_content_size(), buffer.insts_remaining()); - LogTarget(Info, stubs) lt; + if (stub_data.is_dumping()) { + // save the blob and publish the entry addresses + if (stub_data.store_code_blob(*stubs_code, &buffer)) { + if (lt.is_enabled()) { + LogStream ls(lt); + ls.print_cr("Stored blob '%s' to Startup Code Cache", buffer_name); + } + } else { + if (lt.is_enabled()) { + LogStream ls(lt); + ls.print_cr("Failed to store blob '%s' to Startup Code Cache", buffer_name); + } + } + } else if (stub_data.is_open()) { + // we either loaded some entries or generated new entries so + // publish all entries + // + // TODO - ensure we publish collect and publish the preuniverse + // stubs but don't try to save them + AOTCodeCache::publish_stub_addresses(*stubs_code, blob_id, &stub_data); + if (lt.is_enabled()) { + LogStream ls(lt); + ls.print_cr("Republished entries for blob '%s'", buffer_name); + } + } + + // close off recording of any further stubgen generation + if (blob_id == BlobId::stubgen_final_id) { + AOTCodeCache::set_stubgen_stubs_complete(); + } + if (lt.is_enabled()) { LogStream ls(lt); ls.print_cr("%s\t [" INTPTR_FORMAT ", " INTPTR_FORMAT "] used: %d, free: %d", @@ -214,17 +282,19 @@ static BufferBlob* initialize_stubs(BlobId blob_id, return stubs_code; } +// per blob initializer methods StubRoutines::initialize_xxx_stubs() + #define DEFINE_BLOB_INIT_METHOD(blob_name) \ void StubRoutines::initialize_ ## blob_name ## _stubs() { \ if (STUBGEN_BLOB_FIELD_NAME(blob_name) == nullptr) { \ BlobId blob_id = BlobId:: JOIN3(stubgen, blob_name, id); \ int size = _ ## blob_name ## _code_size; \ - int max_aligned_size = 10; \ + int max_aligned_stubs = StubInfo::stub_count(blob_id); \ const char* timer_msg = "StubRoutines generation " # blob_name " stubs"; \ const char* name = "StubRoutines (" # blob_name " stubs)"; \ const char* assert_msg = "_" # blob_name "_code_size"; \ STUBGEN_BLOB_FIELD_NAME(blob_name) = \ - initialize_stubs(blob_id, size, max_aligned_size, timer_msg, \ + initialize_stubs(blob_id, size, max_aligned_stubs, timer_msg, \ name, assert_msg); \ } \ } @@ -234,6 +304,7 @@ STUBGEN_BLOBS_DO(DEFINE_BLOB_INIT_METHOD) #undef DEFINE_BLOB_INIT_METHOD +// external driver API functions for per blob init: xxx_stubs_init() #define DEFINE_BLOB_INIT_FUNCTION(blob_name) \ void blob_name ## _stubs_init() { \ @@ -244,11 +315,18 @@ STUBGEN_BLOBS_DO(DEFINE_BLOB_INIT_FUNCTION) #undef DEFINE_BLOB_INIT_FUNCTION + +#if INCLUDE_CDS +// non-generated external API init driver function + +void stubs_AOTAddressTable_init() { StubRoutines::init_AOTAddressTable(); } +#endif // INCLUDE_CDS + /* - * we generate the underlying driver method but this wrapper is needed - * to perform special handling depending on where the compiler init - * gets called from. it ought to be possible to remove this at some - * point and have a determinate ordered init. + * we generate the underlying driver function compiler_stubs_init() + * but this wrapper is needed to perform special handling depending on + * where the compiler init gets called from. it ought to be possible + * to remove this at some point and have a determinate ordered init. */ void compiler_stubs_init(bool in_compiler_thread) { diff --git a/src/hotspot/share/runtime/stubRoutines.hpp b/src/hotspot/share/runtime/stubRoutines.hpp index 97e3e46b870..894bd47faab 100644 --- a/src/hotspot/share/runtime/stubRoutines.hpp +++ b/src/hotspot/share/runtime/stubRoutines.hpp @@ -112,6 +112,8 @@ class UnsafeMemoryAccess : public CHeapObj { address _end_pc; address _error_exit_pc; public: + // each table entry requires 3 addresses + static const int COLUMN_COUNT = 3; static address _common_exit_stub_pc; static UnsafeMemoryAccess* _table; static int _table_length; @@ -130,6 +132,7 @@ class UnsafeMemoryAccess : public CHeapObj { static UnsafeMemoryAccess* add_to_table(address start_pc, address end_pc, address error_exit_pc) { guarantee(_table_length < _table_max_length, "Incorrect UnsafeMemoryAccess::_table_max_length"); UnsafeMemoryAccess* entry = &_table[_table_length]; + assert(start_pc != nullptr, "invalid start address"); entry->set_start_pc(start_pc); entry->set_end_pc(end_pc); entry->set_error_exit_pc(error_exit_pc); @@ -283,6 +286,11 @@ public: static BlobId stub_to_blob(StubId id); #endif +#if INCLUDE_CDS + // AOT Initalization -- implementation is arch-specific + static void init_AOTAddressTable(); +#endif // INCLUDE_CDS + // Debugging static jint verify_oop_count() { return _verify_oop_count; } static jint* verify_oop_count_addr() { return &_verify_oop_count; } diff --git a/src/hotspot/share/runtime/synchronizer.cpp b/src/hotspot/share/runtime/synchronizer.cpp index e3293f94eeb..fa178dcb5a1 100644 --- a/src/hotspot/share/runtime/synchronizer.cpp +++ b/src/hotspot/share/runtime/synchronizer.cpp @@ -830,8 +830,7 @@ void ObjectSynchronizer::owned_monitors_iterate_filtered(MonitorClosure* closure }); } -// Iterate ObjectMonitors where the owner == thread; this does NOT include -// ObjectMonitors where owner is set to a stack-lock address in thread. +// Iterate ObjectMonitors where the owner == thread. void ObjectSynchronizer::owned_monitors_iterate(MonitorClosure* closure, JavaThread* thread) { int64_t key = ObjectMonitor::owner_id_from(thread); auto thread_filter = [&](ObjectMonitor* monitor) { return monitor->owner() == key; }; @@ -1964,12 +1963,10 @@ ObjectMonitor* ObjectSynchronizer::inflate_into_object_header(oop object, Object const markWord mark = object->mark_acquire(); // The mark can be in one of the following states: - // * inflated - Just return if using stack-locking. - // If using fast-locking and the ObjectMonitor owner - // is anonymous and the locking_thread owns the - // object lock, then we make the locking_thread - // the ObjectMonitor owner and remove the lock from - // the locking_thread's lock stack. + // * inflated - If the ObjectMonitor owner is anonymous and the + // locking_thread owns the object lock, then we make the + // locking_thread the ObjectMonitor owner and remove the + // lock from the locking_thread's lock stack. // * fast-locked - Coerce it to inflated from fast-locked. // * unlocked - Aggressively inflate the object. diff --git a/src/hotspot/share/runtime/threadSMR.cpp b/src/hotspot/share/runtime/threadSMR.cpp index 4c68648fec8..d70b7dcbd34 100644 --- a/src/hotspot/share/runtime/threadSMR.cpp +++ b/src/hotspot/share/runtime/threadSMR.cpp @@ -667,7 +667,7 @@ ThreadsList::ThreadsList(int entries) : ThreadsList::~ThreadsList() { if (_threads != empty_threads_list_data) { - FREE_C_HEAP_ARRAY(JavaThread*, _threads); + FREE_C_HEAP_ARRAY(_threads); } _magic = 0xDEADBEEF; } diff --git a/src/hotspot/share/runtime/threads.cpp b/src/hotspot/share/runtime/threads.cpp index 442b68e596a..27c4c588429 100644 --- a/src/hotspot/share/runtime/threads.cpp +++ b/src/hotspot/share/runtime/threads.cpp @@ -113,6 +113,7 @@ #endif #ifdef COMPILER2 #include "opto/idealGraphPrinter.hpp" +#include "runtime/hotCodeCollector.hpp" #endif #if INCLUDE_JFR #include "jfr/jfr.hpp" @@ -798,6 +799,12 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) { StringDedup::start(); } +#ifdef COMPILER2 + if (HotCodeHeap) { + HotCodeCollector::initialize(); + } +#endif // COMPILER2 + // Pre-initialize some JSR292 core classes to avoid deadlock during class loading. // It is done after compilers are initialized, because otherwise compilations of // signature polymorphic MH intrinsics can be missed @@ -1055,7 +1062,7 @@ void Threads::destroy_vm() { #if INCLUDE_JVMCI if (JVMCICounterSize > 0) { - FREE_C_HEAP_ARRAY(jlong, JavaThread::_jvmci_old_thread_counters); + FREE_C_HEAP_ARRAY(JavaThread::_jvmci_old_thread_counters); } #endif diff --git a/src/hotspot/share/runtime/vmOperation.hpp b/src/hotspot/share/runtime/vmOperation.hpp index 5140d0401fb..6078600c16e 100644 --- a/src/hotspot/share/runtime/vmOperation.hpp +++ b/src/hotspot/share/runtime/vmOperation.hpp @@ -90,7 +90,7 @@ template(ShenandoahFinalMarkStartEvac) \ template(ShenandoahInitUpdateRefs) \ template(ShenandoahFinalUpdateRefs) \ - template(ShenandoahFinalRoots) \ + template(ShenandoahFinalVerify) \ template(ShenandoahDegeneratedGC) \ template(Exit) \ template(LinuxDllLoad) \ diff --git a/src/hotspot/share/runtime/vmOperations.cpp b/src/hotspot/share/runtime/vmOperations.cpp index ef480f04c57..c4a77ce3275 100644 --- a/src/hotspot/share/runtime/vmOperations.cpp +++ b/src/hotspot/share/runtime/vmOperations.cpp @@ -276,9 +276,6 @@ void VM_ThreadDump::doit_epilogue() { } // Hash table of int64_t to a list of ObjectMonitor* owned by the JavaThread. -// The JavaThread's owner key is either a JavaThread* or a stack lock -// address in the JavaThread so we use "int64_t". -// class ObjectMonitorsDump : public MonitorClosure, public ObjectMonitorsView { private: static unsigned int ptr_hash(int64_t const& s1) { diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp index 93e0ff2f3b6..6ae9de05518 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -163,8 +163,7 @@ /******************************************************************/ \ \ volatile_nonstatic_field(oopDesc, _mark, markWord) \ - volatile_nonstatic_field(oopDesc, _metadata._klass, Klass*) \ - volatile_nonstatic_field(oopDesc, _metadata._compressed_klass, narrowKlass) \ + volatile_nonstatic_field(oopDesc, _compressed_klass, narrowKlass) \ static_field(BarrierSet, _barrier_set, BarrierSet*) \ nonstatic_field(ArrayKlass, _dimension, const int) \ volatile_nonstatic_field(ArrayKlass, _higher_dimension, ObjArrayKlass*) \ @@ -335,11 +334,11 @@ nonstatic_field(ThreadLocalAllocBuffer, _pf_top, HeapWord*) \ nonstatic_field(ThreadLocalAllocBuffer, _desired_size, size_t) \ nonstatic_field(ThreadLocalAllocBuffer, _refill_waste_limit, size_t) \ - static_field(ThreadLocalAllocBuffer, _target_refills, unsigned) \ - nonstatic_field(ThreadLocalAllocBuffer, _number_of_refills, unsigned) \ + static_field(ThreadLocalAllocBuffer, _target_num_refills, unsigned) \ + nonstatic_field(ThreadLocalAllocBuffer, _num_refills, unsigned) \ nonstatic_field(ThreadLocalAllocBuffer, _refill_waste, unsigned) \ nonstatic_field(ThreadLocalAllocBuffer, _gc_waste, unsigned) \ - nonstatic_field(ThreadLocalAllocBuffer, _slow_allocations, unsigned) \ + nonstatic_field(ThreadLocalAllocBuffer, _num_slow_allocations, unsigned) \ nonstatic_field(VirtualSpace, _low_boundary, char*) \ nonstatic_field(VirtualSpace, _high_boundary, char*) \ nonstatic_field(VirtualSpace, _low, char*) \ @@ -475,6 +474,7 @@ /***********************************/ \ \ static_field(StubRoutines, _call_stub_return_address, address) \ + static_field(StubRoutines, _cont_returnBarrier, address) \ \ /***************************************/ \ /* PcDesc and other compiled code info */ \ @@ -786,6 +786,7 @@ static_field(Mutex, _mutex_array, Mutex**) \ static_field(Mutex, _num_mutex, int) \ volatile_nonstatic_field(Mutex, _owner, Thread*) \ + nonstatic_field(ContinuationEntry, _parent, ContinuationEntry*) \ static_field(ContinuationEntry, _return_pc, address) //-------------------------------------------------------------------------------- @@ -2085,10 +2086,10 @@ static int recursiveFindType(VMTypeEntry* origtypes, const char* typeName, bool s[len-1] = '\0'; // tty->print_cr("checking \"%s\" for \"%s\"", s, typeName); if (recursiveFindType(origtypes, s, true) == 1) { - FREE_C_HEAP_ARRAY(char, s); + FREE_C_HEAP_ARRAY(s); return 1; } - FREE_C_HEAP_ARRAY(char, s); + FREE_C_HEAP_ARRAY(s); } const char* start = nullptr; if (strstr(typeName, "GrowableArray<") == typeName) { @@ -2104,10 +2105,10 @@ static int recursiveFindType(VMTypeEntry* origtypes, const char* typeName, bool s[len-1] = '\0'; // tty->print_cr("checking \"%s\" for \"%s\"", s, typeName); if (recursiveFindType(origtypes, s, true) == 1) { - FREE_C_HEAP_ARRAY(char, s); + FREE_C_HEAP_ARRAY(s); return 1; } - FREE_C_HEAP_ARRAY(char, s); + FREE_C_HEAP_ARRAY(s); } if (strstr(typeName, "const ") == typeName) { const char * s = typeName + strlen("const "); @@ -2142,4 +2143,3 @@ void vmStructs_init() { VMStructs::init(); } #endif // ASSERT - diff --git a/src/hotspot/share/services/diagnosticArgument.cpp b/src/hotspot/share/services/diagnosticArgument.cpp index 247ab50bde7..0ba7bda2719 100644 --- a/src/hotspot/share/services/diagnosticArgument.cpp +++ b/src/hotspot/share/services/diagnosticArgument.cpp @@ -36,7 +36,7 @@ StringArrayArgument::StringArrayArgument() { StringArrayArgument::~StringArrayArgument() { for (int i=0; i<_array->length(); i++) { - FREE_C_HEAP_ARRAY(char, _array->at(i)); + FREE_C_HEAP_ARRAY(_array->at(i)); } delete _array; } @@ -183,7 +183,7 @@ template <> void DCmdArgument::init_value(TRAPS) { template <> void DCmdArgument::destroy_value() { } template <> void DCmdArgument::destroy_value() { - FREE_C_HEAP_ARRAY(char, _value); + FREE_C_HEAP_ARRAY(_value); set_value(nullptr); } @@ -194,14 +194,14 @@ template <> void DCmdArgument::parse_value(const char* str, } else { // Use realloc as we may have a default set. if (strcmp(type(), "FILE") == 0) { - _value = REALLOC_C_HEAP_ARRAY(char, _value, JVM_MAXPATHLEN, mtInternal); + _value = REALLOC_C_HEAP_ARRAY(_value, JVM_MAXPATHLEN, mtInternal); if (!Arguments::copy_expand_pid(str, len, _value, JVM_MAXPATHLEN)) { stringStream error_msg; error_msg.print("File path invalid or too long: %s", str); THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), error_msg.base()); } } else { - _value = REALLOC_C_HEAP_ARRAY(char, _value, len + 1, mtInternal); + _value = REALLOC_C_HEAP_ARRAY(_value, len + 1, mtInternal); int n = os::snprintf(_value, len + 1, "%.*s", (int)len, str); assert((size_t)n <= len, "Unexpected number of characters in string"); } diff --git a/src/hotspot/share/services/diagnosticCommand.cpp b/src/hotspot/share/services/diagnosticCommand.cpp index 0846f339227..f2fa114133e 100644 --- a/src/hotspot/share/services/diagnosticCommand.cpp +++ b/src/hotspot/share/services/diagnosticCommand.cpp @@ -99,6 +99,7 @@ void DCmd::register_dcmds() { DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export)); DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export)); DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export)); + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export)); DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export)); DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export)); DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export)); @@ -334,8 +335,8 @@ void JVMTIAgentLoadDCmd::execute(DCmdSource source, TRAPS) { #endif // INCLUDE_JVMTI #endif // INCLUDE_SERVICES -void PrintSystemPropertiesDCmd::execute(DCmdSource source, TRAPS) { - // load VMSupport +// helper method for printing system and security properties +static void print_properties(Symbol* method_name, outputStream* out, TRAPS) { Symbol* klass = vmSymbols::jdk_internal_vm_VMSupport(); Klass* k = SystemDictionary::resolve_or_fail(klass, true, CHECK); InstanceKlass* ik = InstanceKlass::cast(k); @@ -343,39 +344,36 @@ void PrintSystemPropertiesDCmd::execute(DCmdSource source, TRAPS) { ik->initialize(THREAD); } if (HAS_PENDING_EXCEPTION) { - java_lang_Throwable::print(PENDING_EXCEPTION, output()); - output()->cr(); + java_lang_Throwable::print(PENDING_EXCEPTION, out); + out->cr(); CLEAR_PENDING_EXCEPTION; return; } - - // invoke the serializePropertiesToByteArray method JavaValue result(T_OBJECT); JavaCallArguments args; - Symbol* signature = vmSymbols::void_byte_array_signature(); - JavaCalls::call_static(&result, - ik, - vmSymbols::serializePropertiesToByteArray_name(), - signature, - &args, - THREAD); + JavaCalls::call_static(&result, ik, method_name, signature, &args, THREAD); + if (HAS_PENDING_EXCEPTION) { - java_lang_Throwable::print(PENDING_EXCEPTION, output()); - output()->cr(); + java_lang_Throwable::print(PENDING_EXCEPTION, out); + out->cr(); CLEAR_PENDING_EXCEPTION; return; } - - // The result should be a [B oop res = result.get_oop(); - assert(res->is_typeArray(), "just checking"); - assert(TypeArrayKlass::cast(res->klass())->element_type() == T_BYTE, "just checking"); - - // copy the bytes to the output stream + assert(res->is_typeArray(), "should be a byte array"); + assert(TypeArrayKlass::cast(res->klass())->element_type() == T_BYTE, "should be a byte array"); typeArrayOop ba = typeArrayOop(res); - jbyte* addr = typeArrayOop(res)->byte_at_addr(0); - output()->print_raw((const char*)addr, ba->length()); + jbyte* addr = ba->byte_at_addr(0); + out->print_raw((const char*)addr, ba->length()); +} + +void PrintSystemPropertiesDCmd::execute(DCmdSource source, TRAPS) { + print_properties(vmSymbols::serializePropertiesToByteArray_name(), output(), THREAD); +} + +void PrintSecurityPropertiesDCmd::execute(DCmdSource source, TRAPS) { + print_properties(vmSymbols::serializeSecurityPropertiesToByteArray_name(), output(), THREAD); } VMUptimeDCmd::VMUptimeDCmd(outputStream* output, bool heap) : diff --git a/src/hotspot/share/services/diagnosticCommand.hpp b/src/hotspot/share/services/diagnosticCommand.hpp index c41e7bf2e2e..97ceb19d0ad 100644 --- a/src/hotspot/share/services/diagnosticCommand.hpp +++ b/src/hotspot/share/services/diagnosticCommand.hpp @@ -94,6 +94,15 @@ public: virtual void execute(DCmdSource source, TRAPS); }; +class PrintSecurityPropertiesDCmd : public DCmd { +public: + PrintSecurityPropertiesDCmd(outputStream* output, bool heap) : DCmd(output, heap) { } + static const char* name() { return "VM.security_properties"; } + static const char* description() { return "Print java.security.Security properties."; } + static const char* impact() { return "Low"; } + virtual void execute(DCmdSource source, TRAPS); +}; + // See also: print_flag in attachListener.cpp class PrintVMFlagsDCmd : public DCmdWithParser { protected: diff --git a/src/hotspot/share/services/finalizerService.cpp b/src/hotspot/share/services/finalizerService.cpp index 9acf17b8cfd..d57d0fb5b50 100644 --- a/src/hotspot/share/services/finalizerService.cpp +++ b/src/hotspot/share/services/finalizerService.cpp @@ -93,7 +93,7 @@ FinalizerEntry::FinalizerEntry(const InstanceKlass* ik) : _total_finalizers_run(0) {} FinalizerEntry::~FinalizerEntry() { - FREE_C_HEAP_ARRAY(char, _codesource); + FREE_C_HEAP_ARRAY(_codesource); } const InstanceKlass* FinalizerEntry::klass() const { diff --git a/src/hotspot/share/services/heapDumper.cpp b/src/hotspot/share/services/heapDumper.cpp index bfb4546a8a1..c850a3a711f 100644 --- a/src/hotspot/share/services/heapDumper.cpp +++ b/src/hotspot/share/services/heapDumper.cpp @@ -2308,7 +2308,7 @@ class VM_HeapDumper : public VM_GC_Operation, public WorkerTask, public Unmounte for (int i = 0; i < _thread_dumpers_count; i++) { delete _thread_dumpers[i]; } - FREE_C_HEAP_ARRAY(ThreadDumper*, _thread_dumpers); + FREE_C_HEAP_ARRAY(_thread_dumpers); } if (_dumper_controller != nullptr) { diff --git a/src/hotspot/share/services/management.cpp b/src/hotspot/share/services/management.cpp index 664fb5a8ef3..36db3df056f 100644 --- a/src/hotspot/share/services/management.cpp +++ b/src/hotspot/share/services/management.cpp @@ -1715,7 +1715,7 @@ ThreadTimesClosure::~ThreadTimesClosure() { for (int i = 0; i < _count; i++) { os::free(_names_chars[i]); } - FREE_C_HEAP_ARRAY(char *, _names_chars); + FREE_C_HEAP_ARRAY(_names_chars); } // Fills names with VM internal thread names and times with the corresponding diff --git a/src/hotspot/share/services/memoryManager.cpp b/src/hotspot/share/services/memoryManager.cpp index ef9babbb20d..2d725db85b5 100644 --- a/src/hotspot/share/services/memoryManager.cpp +++ b/src/hotspot/share/services/memoryManager.cpp @@ -163,8 +163,8 @@ GCStatInfo::GCStatInfo(int num_pools) { } GCStatInfo::~GCStatInfo() { - FREE_C_HEAP_ARRAY(MemoryUsage*, _before_gc_usage_array); - FREE_C_HEAP_ARRAY(MemoryUsage*, _after_gc_usage_array); + FREE_C_HEAP_ARRAY(_before_gc_usage_array); + FREE_C_HEAP_ARRAY(_after_gc_usage_array); } void GCStatInfo::set_gc_usage(int pool_index, MemoryUsage usage, bool before_gc) { diff --git a/src/hotspot/share/services/memoryService.cpp b/src/hotspot/share/services/memoryService.cpp index f64da3c5477..4636f125079 100644 --- a/src/hotspot/share/services/memoryService.cpp +++ b/src/hotspot/share/services/memoryService.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -119,11 +119,11 @@ void MemoryService::add_metaspace_memory_pools() { mgr->add_pool(_metaspace_pool); _pools_list->append(_metaspace_pool); - if (UseCompressedClassPointers) { - _compressed_class_pool = new CompressedKlassSpacePool(); - mgr->add_pool(_compressed_class_pool); - _pools_list->append(_compressed_class_pool); - } +#if INCLUDE_CLASS_SPACE + _compressed_class_pool = new CompressedKlassSpacePool(); + mgr->add_pool(_compressed_class_pool); + _pools_list->append(_compressed_class_pool); +#endif _managers_list->append(mgr); } diff --git a/src/hotspot/share/utilities/concurrentHashTable.inline.hpp b/src/hotspot/share/utilities/concurrentHashTable.inline.hpp index 62d2dd29dab..d5f6dee336b 100644 --- a/src/hotspot/share/utilities/concurrentHashTable.inline.hpp +++ b/src/hotspot/share/utilities/concurrentHashTable.inline.hpp @@ -217,7 +217,7 @@ template inline ConcurrentHashTable:: InternalTable::~InternalTable() { - FREE_C_HEAP_ARRAY(Bucket, _buckets); + FREE_C_HEAP_ARRAY(_buckets); } // ScopedCS diff --git a/src/hotspot/share/utilities/debug.cpp b/src/hotspot/share/utilities/debug.cpp index e8533e29460..23e8281f000 100644 --- a/src/hotspot/share/utilities/debug.cpp +++ b/src/hotspot/share/utilities/debug.cpp @@ -342,20 +342,20 @@ class Command : public StackObj { int Command::level = 0; -extern "C" DEBUGEXPORT void blob(CodeBlob* cb) { +extern "C" NOINLINE void blob(CodeBlob* cb) { Command c("blob"); cb->print(); } -extern "C" DEBUGEXPORT void dump_vtable(address p) { +extern "C" NOINLINE void dump_vtable(address p) { Command c("dump_vtable"); Klass* k = (Klass*)p; k->vtable().print(); } -extern "C" DEBUGEXPORT void nm(intptr_t p) { +extern "C" NOINLINE void nm(intptr_t p) { // Actually we look through all CodeBlobs (the nm name has been kept for backwards compatibility) Command c("nm"); CodeBlob* cb = CodeCache::find_blob((address)p); @@ -367,7 +367,7 @@ extern "C" DEBUGEXPORT void nm(intptr_t p) { } -extern "C" DEBUGEXPORT void disnm(intptr_t p) { +extern "C" NOINLINE void disnm(intptr_t p) { Command c("disnm"); CodeBlob* cb = CodeCache::find_blob((address) p); if (cb != nullptr) { @@ -382,7 +382,7 @@ extern "C" DEBUGEXPORT void disnm(intptr_t p) { } -extern "C" DEBUGEXPORT void printnm(intptr_t p) { +extern "C" NOINLINE void printnm(intptr_t p) { char buffer[256]; os::snprintf_checked(buffer, sizeof(buffer), "printnm: " INTPTR_FORMAT, p); Command c(buffer); @@ -396,14 +396,14 @@ extern "C" DEBUGEXPORT void printnm(intptr_t p) { } -extern "C" DEBUGEXPORT void universe() { +extern "C" NOINLINE void universe() { Command c("universe"); if (!c.onThread()) return; Universe::print_on(tty); } -extern "C" DEBUGEXPORT void verify() { +extern "C" NOINLINE void verify() { // try to run a verify on the entire system // note: this may not be safe if we're not at a safepoint; for debugging, // this manipulates the safepoint settings to avoid assertion failures @@ -421,7 +421,7 @@ extern "C" DEBUGEXPORT void verify() { } -extern "C" DEBUGEXPORT void pp(void* p) { +extern "C" NOINLINE void pp(void* p) { Command c("pp"); if (!c.onThread()) return; FlagSetting fl(DisplayVMOutput, true); @@ -445,7 +445,7 @@ extern "C" DEBUGEXPORT void pp(void* p) { } -extern "C" DEBUGEXPORT void ps() { // print stack +extern "C" NOINLINE void ps() { // print stack // Prints the stack of the current Java thread Command c("ps"); if (!c.onThread()) return; @@ -477,7 +477,7 @@ extern "C" DEBUGEXPORT void ps() { // print stack } } -extern "C" DEBUGEXPORT void pfl() { +extern "C" NOINLINE void pfl() { // print frame layout Command c("pfl"); if (!c.onThread()) return; @@ -494,7 +494,7 @@ extern "C" DEBUGEXPORT void pfl() { } } -extern "C" DEBUGEXPORT void psf() { // print stack frames +extern "C" NOINLINE void psf() { // print stack frames Command c("psf"); if (!c.onThread()) return; JavaThread* p = JavaThread::active(); @@ -511,21 +511,21 @@ extern "C" DEBUGEXPORT void psf() { // print stack frames } -extern "C" DEBUGEXPORT void threads() { +extern "C" NOINLINE void threads() { Command c("threads"); if (!c.onThread()) return; Threads::print(false, true); } -extern "C" DEBUGEXPORT void psd() { +extern "C" NOINLINE void psd() { Command c("psd"); if (!c.onThread()) return; SystemDictionary::print(); } -extern "C" DEBUGEXPORT void pss() { // print all stacks +extern "C" NOINLINE void pss() { // print all stacks Command c("pss"); if (!c.onThread()) return; Threads::print(true, PRODUCT_ONLY(false) NOT_PRODUCT(true)); @@ -533,7 +533,7 @@ extern "C" DEBUGEXPORT void pss() { // print all stacks // #ifndef PRODUCT -extern "C" DEBUGEXPORT void debug() { // to set things up for compiler debugging +extern "C" NOINLINE void debug() { // to set things up for compiler debugging Command c("debug"); NOT_PRODUCT(WizardMode = true;) PrintCompilation = true; @@ -542,7 +542,7 @@ extern "C" DEBUGEXPORT void debug() { // to set things up for comp } -extern "C" DEBUGEXPORT void ndebug() { // undo debug() +extern "C" NOINLINE void ndebug() { // undo debug() Command c("ndebug"); PrintCompilation = false; PrintInlining = PrintAssembly = false; @@ -550,36 +550,36 @@ extern "C" DEBUGEXPORT void ndebug() { // undo debug() } -extern "C" DEBUGEXPORT void flush() { +extern "C" NOINLINE void flush() { Command c("flush"); tty->flush(); } -extern "C" DEBUGEXPORT void events() { +extern "C" NOINLINE void events() { Command c("events"); Events::print(); } -extern "C" DEBUGEXPORT Method* findm(intptr_t pc) { +extern "C" NOINLINE Method* findm(intptr_t pc) { Command c("findm"); nmethod* nm = CodeCache::find_nmethod((address)pc); return (nm == nullptr) ? (Method*)nullptr : nm->method(); } -extern "C" DEBUGEXPORT nmethod* findnm(intptr_t addr) { +extern "C" NOINLINE nmethod* findnm(intptr_t addr) { Command c("findnm"); return CodeCache::find_nmethod((address)addr); } -extern "C" DEBUGEXPORT void find(intptr_t x) { +extern "C" NOINLINE void find(intptr_t x) { Command c("find"); if (!c.onThread()) return; os::print_location(tty, x, false); } -extern "C" DEBUGEXPORT void findpc(intptr_t x) { +extern "C" NOINLINE void findpc(intptr_t x) { Command c("findpc"); if (!c.onThread()) return; os::print_location(tty, x, true); @@ -591,15 +591,14 @@ extern "C" DEBUGEXPORT void findpc(intptr_t x) { // call findclass("java/lang/Object", 0x3) -> find j.l.Object and disasm all of its methods // call findmethod("*ang/Object*", "wait", 0xff) -> detailed disasm of all "wait" methods in j.l.Object // call findmethod("*ang/Object*", "wait:(*J*)V", 0x1) -> list all "wait" methods in j.l.Object that have a long parameter -extern "C" DEBUGEXPORT void findclass(const char* class_name_pattern, int flags) { +extern "C" NOINLINE void findclass(const char* class_name_pattern, int flags) { Command c("findclass"); if (!c.onThread()) return; ClassPrinter::print_flags_help(tty); ClassPrinter::print_classes(class_name_pattern, flags, tty); } -extern "C" DEBUGEXPORT void findmethod(const char* class_name_pattern, - const char* method_pattern, int flags) { +extern "C" NOINLINE void findmethod(const char* class_name_pattern, const char* method_pattern, int flags) { Command c("findmethod"); if (!c.onThread()) return; ClassPrinter::print_flags_help(tty); @@ -607,7 +606,7 @@ extern "C" DEBUGEXPORT void findmethod(const char* class_name_pattern, } // Need method pointer to find bcp -extern "C" DEBUGEXPORT void findbcp(intptr_t method, intptr_t bcp) { +extern "C" NOINLINE void findbcp(intptr_t method, intptr_t bcp) { Command c("findbcp"); Method* mh = (Method*)method; if (!mh->is_native()) { @@ -618,7 +617,7 @@ extern "C" DEBUGEXPORT void findbcp(intptr_t method, intptr_t bcp) { } // check and decode a single u5 value -extern "C" DEBUGEXPORT u4 u5decode(intptr_t addr) { +extern "C" NOINLINE u4 u5decode(intptr_t addr) { Command c("u5decode"); u1* arr = (u1*)addr; size_t off = 0, lim = 5; @@ -635,9 +634,7 @@ extern "C" DEBUGEXPORT u4 u5decode(intptr_t addr) { // there is no limit on the count of items printed; the // printing stops when an null is printed or at limit. // See documentation for UNSIGNED5::Reader::print(count). -extern "C" DEBUGEXPORT intptr_t u5p(intptr_t addr, - intptr_t limit, - int count) { +extern "C" NOINLINE intptr_t u5p(intptr_t addr, intptr_t limit, int count) { Command c("u5p"); u1* arr = (u1*)addr; if (limit && limit < addr) limit = addr; @@ -650,10 +647,10 @@ extern "C" DEBUGEXPORT intptr_t u5p(intptr_t addr, // int versions of all methods to avoid having to type type casts in the debugger -void pp(intptr_t p) { pp((void*)p); } -void pp(oop p) { pp((void*)p); } +NOINLINE void pp(intptr_t p) { pp((void*)p); } +NOINLINE void pp(oop p) { pp((void*)p); } -extern "C" DEBUGEXPORT void help() { +extern "C" NOINLINE void help() { Command c("help"); tty->print_cr("basic"); tty->print_cr(" pp(void* p) - try to make sense of p"); @@ -709,7 +706,7 @@ extern "C" DEBUGEXPORT void help() { } #ifndef PRODUCT -extern "C" DEBUGEXPORT void pns(void* sp, void* fp, void* pc) { // print native stack +extern "C" NOINLINE void pns(void* sp, void* fp, void* pc) { // print native stack Command c("pns"); if (!c.onThread()) return; static char buf[O_BUFLEN]; @@ -728,7 +725,7 @@ extern "C" DEBUGEXPORT void pns(void* sp, void* fp, void* pc) { // print native // WARNING: Only intended for use when debugging. Do not leave calls to // pns2() in committed source (product or debug). // -extern "C" DEBUGEXPORT void pns2() { // print native stack +extern "C" NOINLINE void pns2() { // print native stack Command c("pns2"); if (!c.onThread()) return; static char buf[O_BUFLEN]; @@ -739,6 +736,43 @@ extern "C" DEBUGEXPORT void pns2() { // print native stack } #endif +// just an exported helper; to avoid link time elimination of the referenced functions +extern "C" JNIEXPORT void JVM_debug_helpers_keeper(void* p1, void* p2, void* p3, intptr_t ip, oop oh, address adr) { + blob((CodeBlob*)p1); + dump_vtable(adr); + nm(ip); + disnm(ip); + printnm(ip); + universe(); + verify(); + pp(p1); + ps(); + pfl(); + psf(); + threads(); + psd(); + pss(); + debug(); + ndebug(); + flush(); + events(); + findm(ip); + findnm(ip); + find(ip); + findpc(ip); + findclass("", 0); + findmethod("", "", 0); + findbcp(ip, ip); + u5decode(ip); + u5p(ip, ip, 0); + pp(ip); + pp(oh); + help(); +#ifndef PRODUCT + pns(p1, p2, p3); + pns2(); +#endif +} // Returns true iff the address p is readable and *(intptr_t*)p != errvalue extern "C" bool dbg_is_safe(const void* p, intptr_t errvalue) { diff --git a/src/hotspot/share/utilities/elfFile.cpp b/src/hotspot/share/utilities/elfFile.cpp index 0b7713e9ca9..e81f9128292 100644 --- a/src/hotspot/share/utilities/elfFile.cpp +++ b/src/hotspot/share/utilities/elfFile.cpp @@ -942,7 +942,7 @@ bool DwarfFile::ArangesCache::add_entry(const AddressDescriptor& descriptor, uin bool DwarfFile::ArangesCache::grow() { size_t new_capacity = _capacity == 0 ? 128 : _capacity * 1.5; - ArangesEntry* new_entries = REALLOC_C_HEAP_ARRAY_RETURN_NULL(ArangesEntry, _entries, new_capacity, mtInternal); + ArangesEntry* new_entries = REALLOC_C_HEAP_ARRAY_RETURN_NULL(_entries, new_capacity, mtInternal); if (new_entries == nullptr) { return false; } diff --git a/src/hotspot/share/utilities/elfFile.hpp b/src/hotspot/share/utilities/elfFile.hpp index 8abd846364c..0bfa821e256 100644 --- a/src/hotspot/share/utilities/elfFile.hpp +++ b/src/hotspot/share/utilities/elfFile.hpp @@ -487,7 +487,7 @@ class DwarfFile : public ElfFile { bool grow(); void free() { if (_entries != nullptr) { - FREE_C_HEAP_ARRAY(ArangesEntry, _entries); + FREE_C_HEAP_ARRAY(_entries); _entries = nullptr; } } diff --git a/src/hotspot/share/utilities/globalDefinitions_visCPP.hpp b/src/hotspot/share/utilities/globalDefinitions_visCPP.hpp index dfd6f2f1880..f106d325c68 100644 --- a/src/hotspot/share/utilities/globalDefinitions_visCPP.hpp +++ b/src/hotspot/share/utilities/globalDefinitions_visCPP.hpp @@ -69,7 +69,9 @@ inline int strncasecmp(const char *s1, const char *s2, size_t n) { // *not* the same as the C99 Annex K strtok_s. VS provides that function // under the name strtok_s_l. Make strtok_r a synonym so we can use that name // in shared code. -const auto strtok_r = strtok_s; +inline char* strtok_r(char* str, const char* delim, char** saveptr) { + return strtok_s(str, delim, saveptr); +} // VS doesn't provide POSIX macros S_ISFIFO or S_IFIFO. It doesn't even // provide _S_ISFIFO, per its usual naming convention for POSIX stuff. But it diff --git a/src/hotspot/share/utilities/growableArray.cpp b/src/hotspot/share/utilities/growableArray.cpp index 6a1cb0b0414..9cc0813a1f6 100644 --- a/src/hotspot/share/utilities/growableArray.cpp +++ b/src/hotspot/share/utilities/growableArray.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -22,6 +22,7 @@ * */ +#include "cds/aotMetaspace.hpp" #include "memory/allocation.inline.hpp" #include "memory/resourceArea.hpp" #include "runtime/javaThread.hpp" @@ -56,7 +57,9 @@ void* GrowableArrayCHeapAllocator::allocate(int max, int element_size, MemTag me } void GrowableArrayCHeapAllocator::deallocate(void* elements) { - FreeHeap(elements); + if (!AOTMetaspace::in_aot_cache(elements)) { + FreeHeap(elements); + } } #ifdef ASSERT diff --git a/src/hotspot/share/utilities/growableArray.hpp b/src/hotspot/share/utilities/growableArray.hpp index e300bea6993..14b54cfc4ea 100644 --- a/src/hotspot/share/utilities/growableArray.hpp +++ b/src/hotspot/share/utilities/growableArray.hpp @@ -116,12 +116,6 @@ protected: ~GrowableArrayView() {} -protected: - // Used by AOTGrowableArray for MetaspaceClosure support. - E** data_addr() { - return &_data; - } - public: bool operator==(const GrowableArrayView& rhs) const { if (_len != rhs._len) @@ -303,6 +297,11 @@ public: } tty->print("}\n"); } + + // MetaspaceClosure support + E** data_addr() { + return &_data; + } }; template @@ -821,6 +820,8 @@ public: this->clear_and_deallocate(); } } + + void assert_on_C_heap() { assert(on_C_heap(), "must be on C heap"); } }; // Leaner GrowableArray for CHeap backed data arrays, with compile-time decided MemTag. diff --git a/src/hotspot/share/utilities/integerCast.hpp b/src/hotspot/share/utilities/integerCast.hpp new file mode 100644 index 00000000000..0715cab18d5 --- /dev/null +++ b/src/hotspot/share/utilities/integerCast.hpp @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2026, 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. + * + */ + +#ifndef SHARE_UTILITIES_INTEGERCAST_HPP +#define SHARE_UTILITIES_INTEGERCAST_HPP + +#include "cppstdlib/limits.hpp" +#include "cppstdlib/type_traits.hpp" +#include "metaprogramming/enableIf.hpp" +#include "utilities/debug.hpp" +#include "utilities/macros.hpp" + +#include + +// Tests whether all values for the From type are within the range of values +// for the To Type. From and To must be integral types. This is used by +// integer_cast to test for tautological conversions. +template), + ENABLE_IF(std::is_integral_v)> +constexpr bool is_always_integer_convertible() { + if constexpr (std::is_signed_v == std::is_signed_v) { + // signed => signed or unsigned => unsigned. + return sizeof(To) >= sizeof(From); + } else if constexpr (std::is_signed_v) { + // signed => unsigned is never tautological, because of negative values. + return false; + } else { + // unsigned => signed. + return sizeof(To) > sizeof(From); + } +} + +// Tests whether the value of from is within the range of values for the To +// type. To and From must be integral types. This is used by integer_cast +// to test whether the conversion should be performed. +template), + ENABLE_IF(std::is_integral_v)> +constexpr bool is_integer_convertible(From from) { + if constexpr (is_always_integer_convertible()) { + // This clause simplifies direct calls and the implementation below. It + // isn't needed by integer_cast, where a tautological call is discarded. + return true; + } else if constexpr (std::is_unsigned_v) { + // unsigned => signed or unsigned => unsigned. + // Convert To::max to corresponding unsigned for compare. + using U = std::make_unsigned_t; + return from <= static_cast(std::numeric_limits::max()); + } else if constexpr (std::is_signed_v) { + // signed => signed. + return ((std::numeric_limits::min() <= from) && + (from <= std::numeric_limits::max())); + } else { + // signed => unsigned. Convert from to corresponding unsigned for compare. + using U = std::make_unsigned_t; + return (0 <= from) && (static_cast(from) <= std::numeric_limits::max()); + } +} + +// Convert the from value to the To type, after a debug-only check that the +// value of from is within the range of values for the To type. To and From +// must be integral types. +// +// permit_tautology determines the behavior when a conversion will always +// succeed because the range of values for the From type is enclosed by the +// range of values for the To type (is_always_integer_convertible() +// is true). If true, the conversion will be performed as requested. If +// false, a compile-time error is produced. The default is false for 64bit +// platforms, true for 32bit platforms. See integer_cast_permit_tautology as +// the preferred way to override the default and always provide a true value. +// +// Unnecessary integer_casts make code harder to understand. Hence the +// compile-time failure for tautological conversions, to alert that a code +// change is making a integer_cast unnecessary. This can be suppressed on a +// per-call basis, because there are cases where a conversion might only +// sometimes be tautological. For example, the types involved may vary by +// platform. Another case is if the operation is in a template with dependent +// types, with the operation only being tautological for some instantiations. +// Suppressing the tautology check is an alternative to possibly complex +// metaprogramming to only perform the integer_cast when necessary. +// +// Despite that, for 32bit platforms the default is to not reject unnecessary +// integer_casts. This is because 64bit platforms are the primary target, and +// are likely to require conversions in some places. However, some of those +// conversions will be tautological on 32bit platforms, such as size_t => uint. +template), + ENABLE_IF(std::is_integral_v)> +constexpr To integer_cast(From from) { + if constexpr (is_always_integer_convertible()) { + static_assert(permit_tautology, "tautological integer_cast"); + } else { +#ifdef ASSERT + if (!is_integer_convertible(from)) { + if constexpr (std::is_signed_v) { + fatal("integer_cast failed: %jd", static_cast(from)); + } else { + fatal("integer_cast failed: %ju", static_cast(from)); + } + } +#endif // ASSERT + } + return static_cast(from); +} + +// Equivalent to "integer_cast(from)", disabling the compile-time +// check for tautological casts. Using this function is prefered to direct +// use of the permit_tautology template parameter for integer_cast, unless the +// choice is computed. +template), + ENABLE_IF(std::is_integral_v)> +constexpr To integer_cast_permit_tautology(From from) { + return integer_cast(from); +} + +// Convert an enumerator to an integral value via static_cast, after a +// debug-only check that the value is within the range for the destination +// type. This is mostly for compatibility with old code. Class scoped enums +// were used to work around ancient compilers that didn't implement class +// scoped static integral constants properly, and HotSpot code still has many +// examples of this. For others it might be sufficient to provide an explicit +// underlying type and either permit implicit conversions or use +// PrimitiveConversion::cast. +template), + ENABLE_IF(std::is_enum_v)> +constexpr To integer_cast(From from) { + using U = std::underlying_type_t; + return integer_cast(static_cast(from)); +} + +#endif // SHARE_UTILITIES_INTEGERCAST_HPP diff --git a/src/hotspot/share/utilities/istream.cpp b/src/hotspot/share/utilities/istream.cpp index ce622c2c282..c78fa5d1efa 100644 --- a/src/hotspot/share/utilities/istream.cpp +++ b/src/hotspot/share/utilities/istream.cpp @@ -265,7 +265,7 @@ bool inputStream::expand_buffer(size_t new_length) { } else { // realloc COV(EXB_R); - new_buf = REALLOC_C_HEAP_ARRAY(char, _buffer, new_length, mtInternal); + new_buf = REALLOC_C_HEAP_ARRAY(_buffer, new_length, mtInternal); assert(new_buf != nullptr, "would have exited VM if OOM"); } diff --git a/src/hotspot/share/utilities/macros.hpp b/src/hotspot/share/utilities/macros.hpp index a03255b5cf3..3621f675ecb 100644 --- a/src/hotspot/share/utilities/macros.hpp +++ b/src/hotspot/share/utilities/macros.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -586,6 +586,18 @@ #define BIG_ENDIAN_ONLY(code) code #endif +#ifdef _LP64 +#define INCLUDE_CLASS_SPACE 1 +#define CLASS_SPACE_ONLY(x) x +#define NOT_CLASS_SPACE(x) +#else +// On 32-bit we use fake "narrow class pointers" which are really just 32-bit pointers, +// but we don't use a class space (would cause too much address space fragmentation) +#define INCLUDE_CLASS_SPACE 0 +#define CLASS_SPACE_ONLY(x) +#define NOT_CLASS_SPACE(x) x +#endif + #define define_pd_global(type, name, value) const type pd_##name = value; // Helper macros for constructing file names for includes. diff --git a/src/hotspot/share/utilities/numberSeq.cpp b/src/hotspot/share/utilities/numberSeq.cpp index 536f6563866..9d4ece105ed 100644 --- a/src/hotspot/share/utilities/numberSeq.cpp +++ b/src/hotspot/share/utilities/numberSeq.cpp @@ -144,7 +144,7 @@ TruncatedSeq::TruncatedSeq(int length, double alpha): } TruncatedSeq::~TruncatedSeq() { - FREE_C_HEAP_ARRAY(double, _sequence); + FREE_C_HEAP_ARRAY(_sequence); } void TruncatedSeq::add(double val) { diff --git a/src/hotspot/share/utilities/ostream.cpp b/src/hotspot/share/utilities/ostream.cpp index ded233d48bf..f052431d55e 100644 --- a/src/hotspot/share/utilities/ostream.cpp +++ b/src/hotspot/share/utilities/ostream.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -369,7 +369,7 @@ void stringStream::grow(size_t new_capacity) { } zero_terminate(); } else { - _buffer = REALLOC_C_HEAP_ARRAY(char, _buffer, new_capacity, mtInternal); + _buffer = REALLOC_C_HEAP_ARRAY(_buffer, new_capacity, mtInternal); _capacity = new_capacity; } } @@ -425,11 +425,6 @@ char* stringStream::as_string(bool c_heap) const { NEW_C_HEAP_ARRAY(char, _written + 1, mtInternal) : NEW_RESOURCE_ARRAY(char, _written + 1); ::memcpy(copy, _buffer, _written); copy[_written] = '\0'; // terminating null - if (c_heap) { - // Need to ensure our content is written to memory before we return - // the pointer to it. - OrderAccess::storestore(); - } return copy; } @@ -442,7 +437,7 @@ char* stringStream::as_string(Arena* arena) const { stringStream::~stringStream() { if (!_is_fixed && _buffer != _small_buffer) { - FREE_C_HEAP_ARRAY(char, _buffer); + FREE_C_HEAP_ARRAY(_buffer); } } @@ -681,7 +676,7 @@ fileStream* defaultStream::open_file(const char* log_name) { } fileStream* file = new (mtInternal) fileStream(try_name); - FREE_C_HEAP_ARRAY(char, try_name); + FREE_C_HEAP_ARRAY(try_name); if (file->is_open()) { return file; } @@ -699,7 +694,7 @@ fileStream* defaultStream::open_file(const char* log_name) { jio_printf("Warning: Forcing option -XX:LogFile=%s\n", try_name); file = new (mtInternal) fileStream(try_name); - FREE_C_HEAP_ARRAY(char, try_name); + FREE_C_HEAP_ARRAY(try_name); if (file->is_open()) { return file; } @@ -1056,7 +1051,7 @@ void bufferedStream::write(const char* s, size_t len) { } } if (buffer_length < end) { - buffer = REALLOC_C_HEAP_ARRAY(char, buffer, end, mtInternal); + buffer = REALLOC_C_HEAP_ARRAY(buffer, end, mtInternal); buffer_length = end; } } @@ -1075,7 +1070,7 @@ char* bufferedStream::as_string() { } bufferedStream::~bufferedStream() { - FREE_C_HEAP_ARRAY(char, buffer); + FREE_C_HEAP_ARRAY(buffer); } #ifndef PRODUCT diff --git a/src/hotspot/share/utilities/rbTree.hpp b/src/hotspot/share/utilities/rbTree.hpp index 9c04ccbe9ab..fb505ebe9ca 100644 --- a/src/hotspot/share/utilities/rbTree.hpp +++ b/src/hotspot/share/utilities/rbTree.hpp @@ -385,6 +385,15 @@ public: void free_node(RBNode* node); + // Updates the key in the given node or node cursor. + // This will never trigger a tree rebalancing. + // The user must ensure that no tree properties are broken: + // There must not exist any node with the new key + // For all nodes with key < old_key, must also have key < new_key + // For all nodes with key > old_key, must also have key > new_key + void update_key(const Cursor& node_cursor, const K& new_key); + void update_key(RBNode* node, const K& new_key); + // Inserts a node with the given key/value into the tree, // if the key already exist, the value is updated instead. // Returns false if and only if allocation of a new node failed. diff --git a/src/hotspot/share/utilities/rbTree.inline.hpp b/src/hotspot/share/utilities/rbTree.inline.hpp index 381cb916405..1bd8ba8faf8 100644 --- a/src/hotspot/share/utilities/rbTree.inline.hpp +++ b/src/hotspot/share/utilities/rbTree.inline.hpp @@ -1147,6 +1147,29 @@ inline void RBTree::free_node(RBNode* node) { _allocator.free(node); } +template +inline void RBTree::update_key(const Cursor& node_cursor, const K& new_key) { + precond(node_cursor.valid()); + precond(node_cursor.found()); + + RBNode* node = node_cursor.node(); + update_key(node, new_key); +} + +template +inline void RBTree::update_key(RBNode* node, const K& new_key) { + precond(node != nullptr); + #ifdef ASSERT + const RBNode* prev = node->prev(); + const RBNode* next = node->next(); + + if (prev != nullptr) assert(COMPARATOR::cmp(new_key, prev->key()) == RBTreeOrdering::GT, "updated key not GT previous node's key."); + if (next != nullptr) assert(COMPARATOR::cmp(new_key, next->key()) == RBTreeOrdering::LT, "updated key not LT next node's key."); + #endif // ASSERT + + node->_key = new_key; +} + template inline bool RBTree::upsert(const K& key, const V& val, const RBNode* hint_node) { Cursor node_cursor = cursor(key, hint_node); diff --git a/src/hotspot/share/utilities/resizableHashTable.hpp b/src/hotspot/share/utilities/resizableHashTable.hpp index a5b53698b11..8e6528f6a3c 100644 --- a/src/hotspot/share/utilities/resizableHashTable.hpp +++ b/src/hotspot/share/utilities/resizableHashTable.hpp @@ -45,7 +45,7 @@ protected: ~ResizeableHashTableStorage() { if (ALLOC_TYPE == C_HEAP) { - FREE_C_HEAP_ARRAY(Node*, _table); + FREE_C_HEAP_ARRAY(_table); } } @@ -151,7 +151,7 @@ public: } if (ALLOC_TYPE == AnyObj::C_HEAP) { - FREE_C_HEAP_ARRAY(Node*, old_table); + FREE_C_HEAP_ARRAY(old_table); } BASE::_table = new_table; BASE::_table_size = new_size; diff --git a/src/hotspot/share/utilities/stack.inline.hpp b/src/hotspot/share/utilities/stack.inline.hpp index 49ccf416629..78e575a0eaf 100644 --- a/src/hotspot/share/utilities/stack.inline.hpp +++ b/src/hotspot/share/utilities/stack.inline.hpp @@ -145,7 +145,7 @@ E* Stack::alloc(size_t bytes) template void Stack::free(E* addr, size_t bytes) { - FREE_C_HEAP_ARRAY(char, (char*) addr); + FREE_C_HEAP_ARRAY((char*) addr); } // Stack is used by the GC code and in some hot paths a lot of the Stack diff --git a/src/hotspot/share/utilities/stringUtils.cpp b/src/hotspot/share/utilities/stringUtils.cpp index 0872ce43d4b..d133d21e52f 100644 --- a/src/hotspot/share/utilities/stringUtils.cpp +++ b/src/hotspot/share/utilities/stringUtils.cpp @@ -125,7 +125,7 @@ bool StringUtils::is_star_match(const char* star_pattern, const char* str) { } StringUtils::CommaSeparatedStringIterator::~CommaSeparatedStringIterator() { - FREE_C_HEAP_ARRAY(char, _list); + FREE_C_HEAP_ARRAY(_list); } ccstrlist StringUtils::CommaSeparatedStringIterator::canonicalize(ccstrlist option_value) { diff --git a/src/hotspot/share/utilities/stringUtils.hpp b/src/hotspot/share/utilities/stringUtils.hpp index c3d21233808..66c8d30c7c0 100644 --- a/src/hotspot/share/utilities/stringUtils.hpp +++ b/src/hotspot/share/utilities/stringUtils.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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,11 +26,7 @@ #define SHARE_UTILITIES_STRINGUTILS_HPP #include "memory/allStatic.hpp" - -#ifdef _WINDOWS - // strtok_s is the Windows thread-safe equivalent of POSIX strtok_r -# define strtok_r strtok_s -#endif +#include "utilities/globalDefinitions.hpp" class StringUtils : AllStatic { public: diff --git a/src/hotspot/share/utilities/vmError.cpp b/src/hotspot/share/utilities/vmError.cpp index 48fae6868ab..1cecdc0cb33 100644 --- a/src/hotspot/share/utilities/vmError.cpp +++ b/src/hotspot/share/utilities/vmError.cpp @@ -533,8 +533,7 @@ static void report_vm_version(outputStream* st, char* buf, int buflen) { "", "", #endif UseCompressedOops ? ", compressed oops" : "", - UseCompactObjectHeaders ? ", compact obj headers" - : (UseCompressedClassPointers ? ", compressed class ptrs" : ""), + UseCompactObjectHeaders ? ", compact obj headers" : "", GCConfig::hs_err_name(), VM_Version::vm_platform_string() ); @@ -1215,7 +1214,7 @@ void VMError::report(outputStream* st, bool _verbose) { CompressedOops::print_mode(st); st->cr(); - STEP_IF("printing compressed klass pointers mode", _verbose && UseCompressedClassPointers) + STEP_IF("printing compressed klass pointers mode", _verbose) CDS_ONLY(AOTMetaspace::print_on(st);) Metaspace::print_compressed_class_space(st); CompressedKlassPointers::print_mode(st); @@ -1328,13 +1327,13 @@ void VMError::report(outputStream* st, bool _verbose) { STEP_IF("printing OS information", _verbose) os::print_os_info(st); - st->cr(); #ifdef __APPLE__ // Avoid large stack allocation on Mac for FD count during signal-handling. os::Bsd::print_open_file_descriptors(st, buf, sizeof(buf)); st->cr(); #else os::print_open_file_descriptors(st); + st->cr(); #endif STEP_IF("printing CPU info", _verbose) @@ -1437,12 +1436,10 @@ void VMError::print_vm_info(outputStream* st) { #endif // STEP("printing compressed class ptrs mode") - if (UseCompressedClassPointers) { - CDS_ONLY(AOTMetaspace::print_on(st);) - Metaspace::print_compressed_class_space(st); - CompressedKlassPointers::print_mode(st); - st->cr(); - } + CDS_ONLY(AOTMetaspace::print_on(st);) + Metaspace::print_compressed_class_space(st); + CompressedKlassPointers::print_mode(st); + st->cr(); // Take heap lock over heap, GC and metaspace printing so that information // is consistent. @@ -1556,7 +1553,6 @@ void VMError::print_vm_info(outputStream* st) { // STEP("printing OS information") os::print_os_info(st); - st->cr(); os::print_open_file_descriptors(st); st->cr(); diff --git a/src/hotspot/share/utilities/xmlstream.cpp b/src/hotspot/share/utilities/xmlstream.cpp index 6dacab1dc25..3ebc1b4b4ac 100644 --- a/src/hotspot/share/utilities/xmlstream.cpp +++ b/src/hotspot/share/utilities/xmlstream.cpp @@ -65,7 +65,7 @@ void xmlStream::initialize(outputStream* out) { #ifdef ASSERT xmlStream::~xmlStream() { - FREE_C_HEAP_ARRAY(char, _element_close_stack_low); + FREE_C_HEAP_ARRAY(_element_close_stack_low); } #endif @@ -169,7 +169,7 @@ void xmlStream::see_tag(const char* tag, bool push) { _element_close_stack_high = new_high; _element_close_stack_low = new_low; _element_close_stack_ptr = new_ptr; - FREE_C_HEAP_ARRAY(char, old_low); + FREE_C_HEAP_ARRAY(old_low); push_ptr = new_ptr - (tag_len+1); } assert(push_ptr >= _element_close_stack_low, "in range"); diff --git a/src/java.base/linux/classes/sun/nio/fs/LinuxFileSystem.java b/src/java.base/linux/classes/sun/nio/fs/LinuxFileSystem.java index 44e957f54fb..ec3e135b8b1 100644 --- a/src/java.base/linux/classes/sun/nio/fs/LinuxFileSystem.java +++ b/src/java.base/linux/classes/sun/nio/fs/LinuxFileSystem.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2026, 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 @@ -139,10 +139,7 @@ class LinuxFileSystem extends UnixFileSystem { int size, long addressToPollForCancel) throws UnixException { - int advice = POSIX_FADV_SEQUENTIAL | // sequential data access - POSIX_FADV_NOREUSE | // will access only once - POSIX_FADV_WILLNEED; // will access in near future - posix_fadvise(src, 0, 0, advice); + posix_fadvise(src, 0, 0, POSIX_FADV_SEQUENTIAL); super.bufferedCopy(dst, src, address, size, addressToPollForCancel); } @@ -151,10 +148,7 @@ class LinuxFileSystem extends UnixFileSystem { int directCopy(int dst, int src, long addressToPollForCancel) throws UnixException { - int advice = POSIX_FADV_SEQUENTIAL | // sequential data access - POSIX_FADV_NOREUSE | // will access only once - POSIX_FADV_WILLNEED; // will access in near future - posix_fadvise(src, 0, 0, advice); + posix_fadvise(src, 0, 0, POSIX_FADV_SEQUENTIAL); return directCopy0(dst, src, addressToPollForCancel); } diff --git a/src/java.base/share/classes/java/lang/Class.java b/src/java.base/share/classes/java/lang/Class.java index 8a2f722f3dd..f15291827d5 100644 --- a/src/java.base/share/classes/java/lang/Class.java +++ b/src/java.base/share/classes/java/lang/Class.java @@ -3841,7 +3841,7 @@ public final class Class implements java.io.Serializable, return false; } - return getNestHost() == c.getNestHost(); + return Reflection.areNestMates(this, c); } private native Class[] getNestMembers0(); diff --git a/src/java.base/share/classes/java/lang/String.java b/src/java.base/share/classes/java/lang/String.java index c6c08ed4473..760f3ebc255 100644 --- a/src/java.base/share/classes/java/lang/String.java +++ b/src/java.base/share/classes/java/lang/String.java @@ -70,9 +70,9 @@ import sun.nio.cs.UTF_8; * string literals in Java programs, such as {@code "abc"}, are * implemented as instances of this class. *

- * Strings are constant; their values cannot be changed after they - * are created. String buffers support mutable strings. - * Because String objects are immutable they can be shared. For example: + * Strings are immutable; their values cannot be changed after they + * are created. Because String objects are immutable they can be shared. + * For example: *

  *     String str = "abc";
  * 

diff --git a/src/java.base/share/classes/java/lang/classfile/package-info.java b/src/java.base/share/classes/java/lang/classfile/package-info.java index 460f6699e7b..8bf5559df0a 100644 --- a/src/java.base/share/classes/java/lang/classfile/package-info.java +++ b/src/java.base/share/classes/java/lang/classfile/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, 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 @@ -493,9 +493,9 @@ * * {@snippet lang="text" : * ClassElement = - * FieldModel*(UtfEntry name, Utf8Entry descriptor) - * | MethodModel*(UtfEntry name, Utf8Entry descriptor) - * | ModuleAttribute?(int flags, ModuleEntry moduleName, UtfEntry moduleVersion, + * FieldModel*(Utf8Entry name, Utf8Entry descriptor) + * | MethodModel*(Utf8Entry name, Utf8Entry descriptor) + * | ModuleAttribute?(int flags, ModuleEntry moduleName, Utf8Entry moduleVersion, * List requires, List opens, * List exports, List provides, * List uses) @@ -588,7 +588,7 @@ * | LabelTarget(Label label) * | LineNumber(int line) * | ExceptionCatch(Label tryStart, Label tryEnd, Label handler, ClassEntry exception) - * | LocalVariable(int slot, UtfEntry name, Utf8Entry type, Label startScope, Label endScope) + * | LocalVariable(int slot, Utf8Entry name, Utf8Entry type, Label startScope, Label endScope) * | LocalVariableType(int slot, Utf8Entry name, Utf8Entry type, Label startScope, Label endScope) * | CharacterRange(int rangeStart, int rangeEnd, int flags, Label startScope, Label endScope) * } diff --git a/src/java.base/share/classes/java/lang/foreign/Linker.java b/src/java.base/share/classes/java/lang/foreign/Linker.java index 1a7d33266aa..f597e4ee52e 100644 --- a/src/java.base/share/classes/java/lang/foreign/Linker.java +++ b/src/java.base/share/classes/java/lang/foreign/Linker.java @@ -833,7 +833,7 @@ public sealed interface Linker permits AbstractLinker { *

* Captured state can be stored in, or retrieved from the capture state segment by * constructing var handles from the {@linkplain #captureStateLayout capture state layout}. - * Some functions require this state the be initialized to a particular value before + * Some functions require this state to be initialized to a particular value before * invoking the downcall. *

* The following example demonstrates the use of this linker option: diff --git a/src/java.base/share/classes/java/lang/foreign/MemorySegment.java b/src/java.base/share/classes/java/lang/foreign/MemorySegment.java index 78098e39a17..70b15bb0cd7 100644 --- a/src/java.base/share/classes/java/lang/foreign/MemorySegment.java +++ b/src/java.base/share/classes/java/lang/foreign/MemorySegment.java @@ -1552,6 +1552,11 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { *

* The {@linkplain MemorySegment#maxByteAlignment() maximum byte alignment} for * the {@code NULL} segment is of 262. + * + * @apiNote Clients should avoid using {@code ==} to compare a segment with + * {@code MemorySegment.NULL}. A segment with address {@code 0L} may be + * {@linkplain #ofAddress(long) created independently} and may therefore + * have a different identity. */ MemorySegment NULL = MemorySegment.ofAddress(0L); diff --git a/src/java.base/share/classes/java/lang/foreign/package-info.java b/src/java.base/share/classes/java/lang/foreign/package-info.java index 438d42ae7d1..2070f0c70a8 100644 --- a/src/java.base/share/classes/java/lang/foreign/package-info.java +++ b/src/java.base/share/classes/java/lang/foreign/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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 @@ -114,7 +114,7 @@ * and we use it to {@linkplain java.lang.foreign.SymbolLookup#findOrThrow(java.lang.String) look up} * the {@code strlen} function in the standard C library; a downcall method handle * targeting said function is subsequently - * {@linkplain java.lang.foreign.Linker#downcallHandle(FunctionDescriptor, Linker.Option...) obtained}. + * {@linkplain java.lang.foreign.Linker#downcallHandle(MemorySegment, FunctionDescriptor, Linker.Option...) obtained}. * To complete the linking successfully, we must provide a * {@link java.lang.foreign.FunctionDescriptor} instance, describing the signature of the * {@code strlen} function. From this information, the linker will uniquely determine diff --git a/src/java.base/share/classes/java/lang/ref/Reference.java b/src/java.base/share/classes/java/lang/ref/Reference.java index 88bdb99dfd6..df46ffe6ca6 100644 --- a/src/java.base/share/classes/java/lang/ref/Reference.java +++ b/src/java.base/share/classes/java/lang/ref/Reference.java @@ -644,12 +644,9 @@ public abstract sealed class Reference<@jdk.internal.RequiresIdentity T> * {@code null}, this method has no effect. * @since 9 */ - @ForceInline + @IntrinsicCandidate public static void reachabilityFence(Object ref) { - // Does nothing. This method is annotated with @ForceInline to eliminate - // most of the overhead that using @DontInline would cause with the - // HotSpot JVM, when this fence is used in a wide variety of situations. - // HotSpot JVM retains the ref and does not GC it before a call to - // this method, because the JIT-compilers do not have GC-only safepoints. + // Does nothing. HotSpot JVM retains the ref and does not GC it before a call to this method. + // Using an intrinsic allows JIT-compilers to further optimize it while retaining the correct semantics. } } diff --git a/src/java.base/share/classes/java/math/BigDecimal.java b/src/java.base/share/classes/java/math/BigDecimal.java index 14d81d30c3d..6e651b4fde2 100644 --- a/src/java.base/share/classes/java/math/BigDecimal.java +++ b/src/java.base/share/classes/java/math/BigDecimal.java @@ -1026,12 +1026,11 @@ public class BigDecimal extends Number implements Comparable { return; } // Normalize - while ((significand & 1) == 0) { // i.e., significand is even - significand >>= 1; - exponent++; - } - int scl = 0; + int nTrailingZeros = Long.numberOfTrailingZeros(significand); + significand >>= nTrailingZeros; + exponent += nTrailingZeros; // Calculate intVal and scale + int scl = 0; BigInteger rb; long compactVal = sign * significand; if (exponent == 0) { diff --git a/src/java.base/share/classes/java/security/Security.java b/src/java.base/share/classes/java/security/Security.java index 30a22b05742..9faa172c8e7 100644 --- a/src/java.base/share/classes/java/security/Security.java +++ b/src/java.base/share/classes/java/security/Security.java @@ -330,6 +330,10 @@ public final class Security { public Properties getInitialProperties() { return initialSecurityProperties; } + @Override + public Properties getCurrentProperties() { + return props; + } }); } diff --git a/src/java.base/share/classes/java/text/SimpleDateFormat.java b/src/java.base/share/classes/java/text/SimpleDateFormat.java index ba73e5b5a86..4c57214dbba 100644 --- a/src/java.base/share/classes/java/text/SimpleDateFormat.java +++ b/src/java.base/share/classes/java/text/SimpleDateFormat.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2026, 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 @@ -41,7 +41,7 @@ package java.text; import java.io.IOException; import java.io.InvalidObjectException; import java.io.ObjectInputStream; -import static java.text.DateFormatSymbols.*; +import java.time.ZoneOffset; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; @@ -57,6 +57,8 @@ import sun.util.calendar.ZoneInfoFile; import sun.util.locale.provider.LocaleProviderAdapter; import sun.util.locale.provider.TimeZoneNameUtility; +import static java.text.DateFormatSymbols.*; + /** * {@code SimpleDateFormat} is a concrete class for formatting and * parsing dates in a locale-sensitive manner. It allows for formatting @@ -1293,15 +1295,22 @@ public class SimpleDateFormat extends DateFormat { case PATTERN_ZONE_NAME: // 'z' if (current == null) { + TimeZone tz = calendar.getTimeZone(); + String tzid = tz.getID(); + int zoneOffset = calendar.get(Calendar.ZONE_OFFSET); + int dstOffset = calendar.get(Calendar.DST_OFFSET) + zoneOffset; + + // Check if an explicit metazone DST offset exists + String explicitDstOffset = TimeZoneNameUtility.explicitDstOffset(tzid); + boolean daylight = explicitDstOffset != null ? + dstOffset == ZoneOffset.of(explicitDstOffset).getTotalSeconds() * 1_000 : + dstOffset != zoneOffset; if (formatData.locale == null || formatData.isZoneStringsSet) { - int zoneIndex = - formatData.getZoneIndex(calendar.getTimeZone().getID()); + int zoneIndex = formatData.getZoneIndex(tzid); if (zoneIndex == -1) { - value = calendar.get(Calendar.ZONE_OFFSET) + - calendar.get(Calendar.DST_OFFSET); - buffer.append(ZoneInfoFile.toCustomID(value)); + buffer.append(ZoneInfoFile.toCustomID(dstOffset)); } else { - int index = (calendar.get(Calendar.DST_OFFSET) == 0) ? 1: 3; + int index = daylight ? 3 : 1; if (count < 4) { // Use the short name index++; @@ -1310,8 +1319,6 @@ public class SimpleDateFormat extends DateFormat { buffer.append(zoneStrings[zoneIndex][index]); } } else { - TimeZone tz = calendar.getTimeZone(); - boolean daylight = (calendar.get(Calendar.DST_OFFSET) != 0); int tzstyle = (count < 4 ? TimeZone.SHORT : TimeZone.LONG); buffer.append(tz.getDisplayName(daylight, tzstyle, formatData.locale)); } diff --git a/src/java.base/share/classes/java/time/ZoneOffset.java b/src/java.base/share/classes/java/time/ZoneOffset.java index 2a45e7cbf82..3bcb75db3e4 100644 --- a/src/java.base/share/classes/java/time/ZoneOffset.java +++ b/src/java.base/share/classes/java/time/ZoneOffset.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2025, Alibaba Group Holding Limited. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -87,9 +87,9 @@ import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicReferenceArray; +import java.util.function.Supplier; import jdk.internal.util.DecimalDigits; -import jdk.internal.vm.annotation.Stable; /** * A time-zone offset from Greenwich/UTC, such as {@code +02:00}. @@ -178,8 +178,13 @@ public final class ZoneOffset /** * The zone rules for an offset will always return this offset. Cache it for efficiency. */ - @Stable - private transient ZoneRules rules; + private final transient LazyConstant rules = + LazyConstant.of(new Supplier() { + @Override + public ZoneRules get() { + return ZoneRules.of(ZoneOffset.this); + } + }); //----------------------------------------------------------------------- /** @@ -521,11 +526,7 @@ public final class ZoneOffset */ @Override public ZoneRules getRules() { - ZoneRules rules = this.rules; - if (rules == null) { - rules = this.rules = ZoneRules.of(this); - } - return rules; + return rules.get(); } @Override diff --git a/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java b/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java index 4708094effb..4594dc6f1dc 100644 --- a/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java +++ b/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2025, Alibaba Group Holding Limited. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -4513,7 +4513,11 @@ public final class DateTimeFormatterBuilder { TemporalAccessor dt = context.getTemporal(); int type = GENERIC; if (!isGeneric) { - if (dt.isSupported(ChronoField.INSTANT_SECONDS)) { + // Check if an explicit metazone DST offset exists + String dstOffset = TimeZoneNameUtility.explicitDstOffset(zname); + if (dt.isSupported(OFFSET_SECONDS) && dstOffset != null) { + type = ZoneOffset.from(dt).equals(ZoneOffset.of(dstOffset)) ? DST : STD; + } else if (dt.isSupported(ChronoField.INSTANT_SECONDS)) { type = zone.getRules().isDaylightSavings(Instant.from(dt)) ? DST : STD; } else if (dt.isSupported(ChronoField.EPOCH_DAY) && dt.isSupported(ChronoField.NANO_OF_DAY)) { diff --git a/src/java.base/share/classes/java/util/HexFormat.java b/src/java.base/share/classes/java/util/HexFormat.java index aebb8b9af52..7d9fe08108d 100644 --- a/src/java.base/share/classes/java/util/HexFormat.java +++ b/src/java.base/share/classes/java/util/HexFormat.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2024, Alibaba Group Holding Limited. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -26,6 +26,7 @@ package java.util; +import jdk.internal.ValueBased; import jdk.internal.access.JavaLangAccess; import jdk.internal.access.SharedSecrets; import jdk.internal.util.HexDigits; @@ -134,7 +135,7 @@ import java.nio.CharBuffer; * @since 17 */ - +@ValueBased public final class HexFormat { // Access to create strings from a byte array. diff --git a/src/java.base/share/classes/java/util/Locale.java b/src/java.base/share/classes/java/util/Locale.java index 682476d8082..6b071cd15b2 100644 --- a/src/java.base/share/classes/java/util/Locale.java +++ b/src/java.base/share/classes/java/util/Locale.java @@ -1932,7 +1932,7 @@ public final class Locale implements Cloneable, Serializable { } /** - * Returns a name for the locale's language that is appropriate for display to the + * Returns a name for {@code this} locale's language that is appropriate for display to the * user. * If possible, the name returned will be localized for the default * {@link Locale.Category#DISPLAY DISPLAY} locale. @@ -1946,14 +1946,15 @@ public final class Locale implements Cloneable, Serializable { * this function falls back on the English name, and uses the ISO code as a last-resort * value. If the locale doesn't specify a language, this function returns the empty string. * - * @return The name of the display language. + * @return The name of the display language appropriate to the default + * {@link Locale.Category#DISPLAY DISPLAY} locale. */ - public final String getDisplayLanguage() { + public String getDisplayLanguage() { return getDisplayLanguage(getDefault(Category.DISPLAY)); } /** - * Returns a name for the locale's language that is appropriate for display to the + * Returns a name for {@code this} locale's language that is appropriate for display to the * user. * If possible, the name returned will be localized according to inLocale. * For example, if the locale is fr_FR and inLocale @@ -1964,7 +1965,7 @@ public final class Locale implements Cloneable, Serializable { * on the ISO code as a last-resort value. If the locale doesn't specify a language, * this function returns the empty string. * - * @param inLocale The locale for which to retrieve the display language. + * @param inLocale The locale in which to localize the display language. * @return The name of the display language appropriate to the given locale. * @throws NullPointerException if {@code inLocale} is {@code null} */ @@ -1973,13 +1974,13 @@ public final class Locale implements Cloneable, Serializable { } /** - * Returns a name for the locale's script that is appropriate for display to + * Returns a name for {@code this} locale's script that is appropriate for display to * the user. If possible, the name will be localized for the default * {@link Locale.Category#DISPLAY DISPLAY} locale. Returns * the empty string if this locale doesn't specify a script code. * - * @return the display name of the script code for the current default - * {@link Locale.Category#DISPLAY DISPLAY} locale + * @return The display name of the script code appropriate to the default + * {@link Locale.Category#DISPLAY DISPLAY} locale. * @since 1.7 */ public String getDisplayScript() { @@ -1987,14 +1988,13 @@ public final class Locale implements Cloneable, Serializable { } /** - * Returns a name for the locale's script that is appropriate + * Returns a name for {@code this} locale's script that is appropriate * for display to the user. If possible, the name will be * localized for the given locale. Returns the empty string if * this locale doesn't specify a script code. * - * @param inLocale The locale for which to retrieve the display script. - * @return the display name of the script code for the current default - * {@link Locale.Category#DISPLAY DISPLAY} locale + * @param inLocale The locale in which to localize the display script. + * @return The display name of the script code appropriate to the given locale. * @throws NullPointerException if {@code inLocale} is {@code null} * @since 1.7 */ @@ -2003,7 +2003,7 @@ public final class Locale implements Cloneable, Serializable { } /** - * Returns a name for the locale's country that is appropriate for display to the + * Returns a name for {@code this} locale's country that is appropriate for display to the * user. * If possible, the name returned will be localized for the default * {@link Locale.Category#DISPLAY DISPLAY} locale. @@ -2017,14 +2017,15 @@ public final class Locale implements Cloneable, Serializable { * this function falls back on the English name, and uses the ISO code as a last-resort * value. If the locale doesn't specify a country, this function returns the empty string. * - * @return The name of the country appropriate to the locale. + * @return The name of the country appropriate to the default + * {@link Locale.Category#DISPLAY DISPLAY} locale. */ - public final String getDisplayCountry() { + public String getDisplayCountry() { return getDisplayCountry(getDefault(Category.DISPLAY)); } /** - * Returns a name for the locale's country that is appropriate for display to the + * Returns a name for {@code this} locale's country that is appropriate for display to the * user. * If possible, the name returned will be localized according to inLocale. * For example, if the locale is fr_FR and inLocale @@ -2035,7 +2036,7 @@ public final class Locale implements Cloneable, Serializable { * on the ISO code as a last-resort value. If the locale doesn't specify a country, * this function returns the empty string. * - * @param inLocale The locale for which to retrieve the display country. + * @param inLocale The locale in which to localize the display country. * @return The name of the country appropriate to the given locale. * @throws NullPointerException if {@code inLocale} is {@code null} */ @@ -2061,23 +2062,24 @@ public final class Locale implements Cloneable, Serializable { } /** - * Returns a name for the locale's variant code that is appropriate for display to the + * Returns a name for {@code this} locale's variant code that is appropriate for display to the * user. If possible, the name will be localized for the default * {@link Locale.Category#DISPLAY DISPLAY} locale. If the locale * doesn't specify a variant code, this function returns the empty string. * - * @return The name of the display variant code appropriate to the locale. + * @return The name of the display variant code appropriate to the default + * {@link Locale.Category#DISPLAY DISPLAY} locale. */ - public final String getDisplayVariant() { + public String getDisplayVariant() { return getDisplayVariant(getDefault(Category.DISPLAY)); } /** - * Returns a name for the locale's variant code that is appropriate for display to the + * Returns a name for {@code this} locale's variant code that is appropriate for display to the * user. If possible, the name will be localized for inLocale. If the locale * doesn't specify a variant code, this function returns the empty string. * - * @param inLocale The locale for which to retrieve the display variant code. + * @param inLocale The locale in which to localize the display variant code. * @return The name of the display variant code appropriate to the given locale. * @throws NullPointerException if {@code inLocale} is {@code null} */ @@ -2098,7 +2100,7 @@ public final class Locale implements Cloneable, Serializable { } /** - * Returns a name for the locale that is appropriate for display to the + * Returns a name for {@code this} locale that is appropriate for display to the * user. This will be the values returned by getDisplayLanguage(), * getDisplayScript(), getDisplayCountry(), getDisplayVariant() and * optional {@linkplain ##def_locale_extension Unicode extensions} @@ -2116,14 +2118,15 @@ public final class Locale implements Cloneable, Serializable { * be localized depending on the locale. If the language, script, country, * and variant fields are all empty, this function returns the empty string. * - * @return The name of the locale appropriate to display. + * @return The display name appropriate to the default + * {@link Locale.Category#DISPLAY DISPLAY} locale. */ - public final String getDisplayName() { + public String getDisplayName() { return getDisplayName(getDefault(Category.DISPLAY)); } /** - * Returns a name for the locale that is appropriate for display + * Returns a name for {@code this} locale that is appropriate for display * to the user. This will be the values returned by * getDisplayLanguage(), getDisplayScript(), getDisplayCountry(), * getDisplayVariant(), and optional {@linkplain ##def_locale_extension @@ -2142,8 +2145,8 @@ public final class Locale implements Cloneable, Serializable { * be localized depending on the locale. If the language, script, country, * and variant fields are all empty, this function returns the empty string. * - * @param inLocale The locale for which to retrieve the display name. - * @return The name of the locale appropriate to display. + * @param inLocale The locale in which to localize the display name. + * @return The display name appropriate to the given locale. * @throws NullPointerException if {@code inLocale} is {@code null} */ public String getDisplayName(Locale inLocale) { diff --git a/src/java.base/share/classes/java/util/ResourceBundle.java b/src/java.base/share/classes/java/util/ResourceBundle.java index db19eda6399..f91db79891b 100644 --- a/src/java.base/share/classes/java/util/ResourceBundle.java +++ b/src/java.base/share/classes/java/util/ResourceBundle.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2026, 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 @@ -1119,15 +1119,15 @@ public abstract class ResourceBundle { * sequence of bundle names generated by truncating the last underscore and * the part following it is inserted after a candidate bundle name with the * original variant. For example, for a locale with language "en", script - * "Latn, country "US" and variant "WINDOWS_VISTA", and bundle base name + * "Latn", country "US" and variant "WINDOWS_WIN11", and bundle base name * "MyResource", the list of candidate bundle names below is generated: * *

-     * MyResource_en_Latn_US_WINDOWS_VISTA
+     * MyResource_en_Latn_US_WINDOWS_WIN11
      * MyResource_en_Latn_US_WINDOWS
      * MyResource_en_Latn_US
      * MyResource_en_Latn
-     * MyResource_en_US_WINDOWS_VISTA
+     * MyResource_en_US_WINDOWS_WIN11
      * MyResource_en_US_WINDOWS
      * MyResource_en_US
      * MyResource_en
diff --git a/src/java.base/share/classes/java/util/ServiceLoader.java b/src/java.base/share/classes/java/util/ServiceLoader.java
index 5137adc1c08..5e4fa4ed2ef 100644
--- a/src/java.base/share/classes/java/util/ServiceLoader.java
+++ b/src/java.base/share/classes/java/util/ServiceLoader.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2026, 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
@@ -621,9 +621,9 @@ public final class ServiceLoader
         Constructor ctor = null;
         try {
             ctor = clazz.getConstructor();
-        } catch (NoSuchMethodException ex) {
+        } catch (NoSuchMethodException | LinkageError e) {
             String cn = clazz.getName();
-            fail(service, cn + " Unable to get public no-arg constructor", ex);
+            fail(service, cn + " Unable to get public no-arg constructor", e);
         }
         if (inExplicitModule(clazz))
             ctor.setAccessible(true);
@@ -1086,8 +1086,8 @@ public final class ServiceLoader
             String cn = pending.next();
             try {
                 return Class.forName(cn, false, loader);
-            } catch (ClassNotFoundException x) {
-                fail(service, "Provider " + cn + " not found");
+            } catch (ClassNotFoundException | LinkageError e) {
+                fail(service, "Provider " + cn + " not found", e);
                 return null;
             }
         }
diff --git a/src/java.base/share/classes/java/util/stream/Stream.java b/src/java.base/share/classes/java/util/stream/Stream.java
index 1dd13133fe1..645f4f033b7 100644
--- a/src/java.base/share/classes/java/util/stream/Stream.java
+++ b/src/java.base/share/classes/java/util/stream/Stream.java
@@ -1002,8 +1002,8 @@ public interface Stream extends BaseStream> {
 
     /**
      * Performs a reduction on the
-     * elements of this stream, using the provided identity, accumulation and
-     * combining functions.  This is equivalent to:
+     * elements of this stream using the provided identity value, accumulation
+     * function, and combining function.  This is equivalent to:
      * 
{@code
      *     U result = identity;
      *     for (T element : this stream)
diff --git a/src/java.base/share/classes/java/util/zip/GZIPInputStream.java b/src/java.base/share/classes/java/util/zip/GZIPInputStream.java
index ebcb9e3204c..72fb8036f08 100644
--- a/src/java.base/share/classes/java/util/zip/GZIPInputStream.java
+++ b/src/java.base/share/classes/java/util/zip/GZIPInputStream.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2026, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 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
@@ -79,11 +79,7 @@ public class GZIPInputStream extends InflaterInputStream {
         super(in, createInflater(in, size), size);
         usesDefaultInflater = true;
         try {
-            // we don't expect the stream to be at EOF
-            // and if it is, then we want readHeader to
-            // raise an exception, so we pass "true" for
-            // the "failOnEOF" param.
-            readHeader(in, true);
+            readHeader(in);
         } catch (IOException ioe) {
             this.inf.end();
             throw ioe;
@@ -194,40 +190,12 @@ public class GZIPInputStream extends InflaterInputStream {
     /*
      * Reads GZIP member header and returns the total byte number
      * of this member header.
-     * If failOnEOF is false and if the given InputStream has already
-     * reached EOF when this method was invoked, then this method returns
-     * -1 (indicating that there's no GZIP member header).
-     * In all other cases of malformed header or EOF being detected
-     * when reading the header, this method will throw an IOException.
      */
-    private int readHeader(InputStream this_in, boolean failOnEOF) throws IOException {
+    private int readHeader(InputStream this_in) throws IOException {
         CheckedInputStream in = new CheckedInputStream(this_in, crc);
         crc.reset();
-
-        int magic;
-        if (!failOnEOF) {
-            // read an unsigned short value representing the GZIP magic header.
-            // this is the same as calling readUShort(in), except that here,
-            // when reading the first byte, we don't raise an EOFException
-            // if the stream has already reached EOF.
-
-            // read unsigned byte
-            int b = in.read();
-            if (b == -1) { // EOF
-                crc.reset();
-                return -1; // represents no header bytes available
-            }
-            checkUnexpectedByte(b);
-            // read the next unsigned byte to form the unsigned
-            // short. we throw the usual EOFException/ZipException
-            // from this point on if there is no more data or
-            // the data doesn't represent a header.
-            magic = (readUByte(in) << 8) | b;
-        } else {
-            magic = readUShort(in);
-        }
         // Check header magic
-        if (magic != GZIP_MAGIC) {
+        if (readUShort(in) != GZIP_MAGIC) {
             throw new ZipException("Not in GZIP format");
         }
         // Check compression method
@@ -290,21 +258,23 @@ public class GZIPInputStream extends InflaterInputStream {
             (readUInt(in) != (inf.getBytesWritten() & 0xffffffffL)))
             throw new ZipException("Corrupt GZIP trailer");
 
+        // If there are more bytes available in "in" or
+        // the leftover in the "inf" is > 26 bytes:
+        // this.trailer(8) + next.header.min(10) + next.trailer(8)
         // try concatenated case
-        int m = 8;                  // this.trailer
-        try {
-            int numNextHeaderBytes = readHeader(in, false); // next.header (if available)
-            if (numNextHeaderBytes == -1) {
-                return true; // end of stream reached
+        if (this.in.available() > 0 || n > 26) {
+            int m = 8;                  // this.trailer
+            try {
+                m += readHeader(in);    // next.header
+            } catch (IOException ze) {
+                return true;  // ignore any malformed, do nothing
             }
-            m += numNextHeaderBytes;
-        } catch (IOException ze) {
-            return true;  // ignore any malformed, do nothing
+            inf.reset();
+            if (n > m)
+                inf.setInput(buf, len - n + m, n - m);
+            return false;
         }
-        inf.reset();
-        if (n > m)
-            inf.setInput(buf, len - n + m, n - m);
-        return false;
+        return true;
     }
 
     /*
@@ -331,16 +301,12 @@ public class GZIPInputStream extends InflaterInputStream {
         if (b == -1) {
             throw new EOFException();
         }
-        checkUnexpectedByte(b);
-        return b;
-    }
-
-    private void checkUnexpectedByte(final int b) throws IOException {
         if (b < -1 || b > 255) {
-            // report the InputStream type which returned this unexpected byte
+            // Report on this.in, not argument in; see read{Header, Trailer}.
             throw new IOException(this.in.getClass().getName()
-                    + ".read() returned value out of range -1..255: " + b);
+                + ".read() returned value out of range -1..255: " + b);
         }
+        return b;
     }
 
     private byte[] tmpbuf = new byte[128];
diff --git a/src/java.base/share/classes/java/util/zip/ZipEntry.java b/src/java.base/share/classes/java/util/zip/ZipEntry.java
index bf0bf55ff98..0206d2a5154 100644
--- a/src/java.base/share/classes/java/util/zip/ZipEntry.java
+++ b/src/java.base/share/classes/java/util/zip/ZipEntry.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1995, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 2026, 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
@@ -651,8 +651,9 @@ public class ZipEntry implements ZipConstants, Cloneable {
     }
 
     /**
-     * Sets the optional comment string for the entry.
-     * @param comment the comment string
+     * Sets the optional comment string for the entry. If {@code comment} is an
+     * empty string or {@code null} then the entry will have no comment.
+     * @param comment the comment string, or an empty string or null for no comment
      * @throws IllegalArgumentException if the combined length
      * of the specified entry comment, the {@linkplain #getName() entry name},
      * the {@linkplain #getExtra() extra field data}, and the
diff --git a/src/java.base/share/classes/java/util/zip/ZipOutputStream.java b/src/java.base/share/classes/java/util/zip/ZipOutputStream.java
index 47499858a37..d79b0a1bd9c 100644
--- a/src/java.base/share/classes/java/util/zip/ZipOutputStream.java
+++ b/src/java.base/share/classes/java/util/zip/ZipOutputStream.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2026, 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
@@ -43,6 +43,10 @@ import sun.nio.cs.UTF_8;
  * 

Unless otherwise noted, passing a {@code null} argument to a constructor * or method in this class will cause a {@link NullPointerException} to be * thrown. + *

By default, the UTF-8 charset is used to encode entry names and comments. + * {@link #ZipOutputStream(OutputStream, Charset)} may be be used to specify + * an alternative charset. + * * @author David Connelly * @since 1.1 */ @@ -110,10 +114,8 @@ public class ZipOutputStream extends DeflaterOutputStream implements ZipConstant public static final int DEFLATED = ZipEntry.DEFLATED; /** - * Creates a new ZIP output stream. - * - *

The UTF-8 {@link java.nio.charset.Charset charset} is used - * to encode the entry names and comments. + * Creates a new ZIP output stream using the UTF-8 + * {@link Charset charset} to encode entry names and comments. * * @param out the actual output stream */ @@ -122,12 +124,13 @@ public class ZipOutputStream extends DeflaterOutputStream implements ZipConstant } /** - * Creates a new ZIP output stream. + * Creates a new ZIP output stream using the specified + * {@link Charset charset} to encode entry names and comments. * * @param out the actual output stream * * @param charset the {@linkplain java.nio.charset.Charset charset} - * to be used to encode the entry names and comments + * to be used to encode entry names and comments * * @since 1.7 */ @@ -140,10 +143,15 @@ public class ZipOutputStream extends DeflaterOutputStream implements ZipConstant } /** - * Sets the ZIP file comment. - * @param comment the comment string - * @throws IllegalArgumentException if the length of the specified - * ZIP file comment is greater than 0xFFFF bytes + * Sets the ZIP file comment. If {@code comment} is an empty string or + * {@code null} then the output will have no ZIP file comment. + * + * @param comment the comment string, or an empty string or null for no comment + * + * @throws IllegalArgumentException if the length of the specified ZIP file + * comment is greater than 0xFFFF bytes or if the {@code comment} + * contains characters that cannot be mapped by the {@code Charset} + * used to encode entry names and comments */ public void setComment(String comment) { byte[] bytes = null; @@ -257,6 +265,11 @@ public class ZipOutputStream extends DeflaterOutputStream implements ZipConstant default: throw new ZipException("unsupported compression method"); } + // Verify that entry name and comment can be encoded + byte[] nameBytes = checkEncodable(e.name, "unmappable character in ZIP entry name"); + if (e.comment != null) { + checkEncodable(e.comment, "unmappable character in ZIP entry comment"); + } if (! names.add(e.name)) { throw new ZipException("duplicate entry: " + e.name); } @@ -270,7 +283,16 @@ public class ZipOutputStream extends DeflaterOutputStream implements ZipConstant } current = new XEntry(e, written); xentries.add(current); - writeLOC(current); + writeLOC(current, nameBytes); + } + + // Throws ZipException if the given string cannot be encoded + private byte[] checkEncodable(String str, String msg) throws ZipException { + try { + return zc.getBytes(str); + } catch (IllegalArgumentException ex) { + throw (ZipException) new ZipException(msg).initCause(ex); + } } /** @@ -424,7 +446,7 @@ public class ZipOutputStream extends DeflaterOutputStream implements ZipConstant /* * Writes local file (LOC) header for specified entry. */ - private void writeLOC(XEntry xentry) throws IOException { + private void writeLOC(XEntry xentry, byte[] nameBytes) throws IOException { ZipEntry e = xentry.entry; int flag = e.flag; boolean hasZip64 = false; @@ -461,7 +483,6 @@ public class ZipOutputStream extends DeflaterOutputStream implements ZipConstant writeInt(e.size); // uncompressed size } } - byte[] nameBytes = zc.getBytes(e.name); writeShort(nameBytes.length); int elenEXTT = 0; // info-zip extended timestamp diff --git a/src/java.base/share/classes/jdk/internal/access/JavaSecurityPropertiesAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaSecurityPropertiesAccess.java index a4875f357e3..2d9dbea052a 100644 --- a/src/java.base/share/classes/jdk/internal/access/JavaSecurityPropertiesAccess.java +++ b/src/java.base/share/classes/jdk/internal/access/JavaSecurityPropertiesAccess.java @@ -29,4 +29,5 @@ import java.util.Properties; public interface JavaSecurityPropertiesAccess { Properties getInitialProperties(); + Properties getCurrentProperties(); } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/ppc64/CallArranger.java b/src/java.base/share/classes/jdk/internal/foreign/abi/ppc64/CallArranger.java index 0fd90ef6f73..c9994ec2930 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/ppc64/CallArranger.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/ppc64/CallArranger.java @@ -245,7 +245,12 @@ public abstract class CallArranger { // Regular struct, no HFA. VMStorage[] structAlloc(MemoryLayout layout) { // Allocate enough gp slots (regs and stack) such that the struct fits in them. - int numChunks = (int) Utils.alignUp(layout.byteSize(), MAX_COPY_SIZE) / MAX_COPY_SIZE; + final int numChunks; + try { + numChunks = Math.toIntExact(Utils.alignUp(layout.byteSize(), MAX_COPY_SIZE) / MAX_COPY_SIZE); + } catch (ArithmeticException ae) { + throw new IllegalArgumentException("Layout too large: " + layout, ae); + } VMStorage[] result = new VMStorage[numChunks]; for (int i = 0; i < numChunks; i++) { result[i] = nextStorage(StorageType.INTEGER, false); diff --git a/src/java.base/share/classes/jdk/internal/misc/MethodFinder.java b/src/java.base/share/classes/jdk/internal/misc/MethodFinder.java index 60895b8115a..1ee608f2caf 100644 --- a/src/java.base/share/classes/jdk/internal/misc/MethodFinder.java +++ b/src/java.base/share/classes/jdk/internal/misc/MethodFinder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, 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 @@ -27,6 +27,7 @@ package jdk.internal.misc; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.util.Objects; import jdk.internal.access.JavaLangAccess; import jdk.internal.access.SharedSecrets; @@ -88,21 +89,27 @@ public class MethodFinder { mainMethod = JLA.findMethod(cls, false, "main", String[].class); } - if (mainMethod == null || !isValidMainMethod(mainMethod)) { + if (mainMethod == null || !isValidMainMethod(cls, mainMethod)) { mainMethod = JLA.findMethod(cls, false, "main"); } - if (mainMethod == null || !isValidMainMethod(mainMethod)) { + if (mainMethod == null || !isValidMainMethod(cls, mainMethod)) { return null; } return mainMethod; } - private static boolean isValidMainMethod(Method mainMethodCandidate) { + private static boolean isValidMainMethod(Class initialClass, Method mainMethodCandidate) { return mainMethodCandidate.getReturnType() == void.class && - !Modifier.isPrivate(mainMethodCandidate.getModifiers()); - + !Modifier.isPrivate(mainMethodCandidate.getModifiers()) && + (Modifier.isPublic(mainMethodCandidate.getModifiers()) || + Modifier.isProtected(mainMethodCandidate.getModifiers()) || + isInSameRuntimePackage(initialClass, mainMethodCandidate.getDeclaringClass())); } + private static boolean isInSameRuntimePackage(Class c1, Class c2) { + return Objects.equals(c1.getPackageName(), c2.getPackageName()) && + c1.getClassLoader() == c2.getClassLoader(); + } } diff --git a/src/java.base/share/classes/jdk/internal/vm/ThreadDumper.java b/src/java.base/share/classes/jdk/internal/vm/ThreadDumper.java index 276c379a564..fa7d4bab076 100644 --- a/src/java.base/share/classes/jdk/internal/vm/ThreadDumper.java +++ b/src/java.base/share/classes/jdk/internal/vm/ThreadDumper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -250,18 +250,43 @@ public class ThreadDumper { } } + /** + * JSON is schema-less and the thread dump format will evolve over time. + * {@code HotSpotDiagnosticMXBean.dumpThreads} links to a JSON file that documents + * the latest/current format. A system property can be used to generate the thread + * dump in older formats if necessary. + */ + private static class JsonFormat { + private static final String JSON_FORMAT_VERSION_PROP = + "com.sun.management.HotSpotDiagnosticMXBean.dumpThreads.format"; + static final int JSON_FORMAT_V1 = 1; + static final int JSON_FORMAT_V2 = 2; + private static final int JSON_FORMAT_LATEST = JSON_FORMAT_V2; + private static final int JSON_FORMAT; + static { + int ver = Integer.getInteger(JSON_FORMAT_VERSION_PROP, JSON_FORMAT_LATEST); + JSON_FORMAT = Math.clamp(ver, JSON_FORMAT_V1, JSON_FORMAT_LATEST); + } + + static int formatVersion() { + return JSON_FORMAT; + } + } + /** * Generate a thread dump to the given text stream in JSON format. * @throws UncheckedIOException if an I/O error occurs */ private static void dumpThreadsToJson(TextWriter textWriter) { - var jsonWriter = new JsonWriter(textWriter); - + int format = JsonFormat.formatVersion(); + var jsonWriter = new JsonWriter(textWriter, (format == JsonFormat.JSON_FORMAT_V1)); jsonWriter.startObject(); // top-level object - jsonWriter.startObject("threadDump"); + if (format > JsonFormat.JSON_FORMAT_V1) { + jsonWriter.writeProperty("formatVersion", format); + } - jsonWriter.writeProperty("processId", processId()); + jsonWriter.writeLongProperty("processId", processId()); jsonWriter.writeProperty("time", Instant.now()); jsonWriter.writeProperty("runtimeVersion", Runtime.version()); @@ -284,7 +309,11 @@ public class ThreadDumper { jsonWriter.writeProperty("parent", container.parent()); Thread owner = container.owner(); - jsonWriter.writeProperty("owner", (owner != null) ? owner.threadId() : null); + if (owner != null) { + jsonWriter.writeLongProperty("owner", owner.threadId()); + } else { + jsonWriter.writeProperty("owner", null); // owner is not optional + } long threadCount = 0; jsonWriter.startArray("threads"); @@ -301,7 +330,7 @@ public class ThreadDumper { if (!ThreadContainers.trackAllThreads()) { threadCount = Long.max(threadCount, container.threadCount()); } - jsonWriter.writeProperty("threadCount", threadCount); + jsonWriter.writeLongProperty("threadCount", threadCount); jsonWriter.endObject(); @@ -324,7 +353,7 @@ public class ThreadDumper { StackTraceElement[] stackTrace = snapshot.stackTrace(); jsonWriter.startObject(); - jsonWriter.writeProperty("tid", thread.threadId()); + jsonWriter.writeLongProperty("tid", thread.threadId()); jsonWriter.writeProperty("time", now); if (thread.isVirtual()) { jsonWriter.writeProperty("virtual", Boolean.TRUE); @@ -339,7 +368,7 @@ public class ThreadDumper { jsonWriter.startObject("parkBlocker"); jsonWriter.writeProperty("object", Objects.toIdentityString(parkBlocker)); if (snapshot.parkBlockerOwner() instanceof Thread owner) { - jsonWriter.writeProperty("owner", owner.threadId()); + jsonWriter.writeLongProperty("owner", owner.threadId()); } jsonWriter.endObject(); } @@ -380,7 +409,7 @@ public class ThreadDumper { // thread identifier of carrier, when mounted if (thread.isVirtual() && snapshot.carrierThread() instanceof Thread carrier) { - jsonWriter.writeProperty("carrier", carrier.threadId()); + jsonWriter.writeLongProperty("carrier", carrier.threadId()); } jsonWriter.endObject(); @@ -411,10 +440,12 @@ public class ThreadDumper { } } private final Deque stack = new ArrayDeque<>(); + private final boolean generateLongsAsString; private final TextWriter writer; - JsonWriter(TextWriter writer) { + JsonWriter(TextWriter writer, boolean generateLongsAsString) { this.writer = writer; + this.generateLongsAsString = generateLongsAsString; } private void indent() { @@ -461,6 +492,7 @@ public class ThreadDumper { */ void writeProperty(String name, Object obj) { Node node = stack.peek(); + assert node != null; if (node.getAndIncrementPropertyCount() > 0) { writer.println(","); } @@ -469,8 +501,6 @@ public class ThreadDumper { writer.print("\"" + name + "\": "); } switch (obj) { - // Long may be larger than safe range of JSON integer value - case Long _ -> writer.print("\"" + obj + "\""); case Number _ -> writer.print(obj); case Boolean _ -> writer.print(obj); case null -> writer.print("null"); @@ -478,6 +508,19 @@ public class ThreadDumper { } } + /** + * Write a property with a long value. If the value is outside the "interop" + * range of IEEE-754 double-precision floating point (64-bit) then it is + * written as a string. + */ + void writeLongProperty(String name, long value) { + if (generateLongsAsString || value < -0x1FFFFFFFFFFFFFL || value > 0x1FFFFFFFFFFFFFL) { + writeProperty(name, Long.toString(value)); + } else { + writeProperty(name, value); + } + } + /** * Write an unnamed property. */ diff --git a/src/java.base/share/classes/jdk/internal/vm/VMSupport.java b/src/java.base/share/classes/jdk/internal/vm/VMSupport.java index 197da0d456c..32c358340af 100644 --- a/src/java.base/share/classes/jdk/internal/vm/VMSupport.java +++ b/src/java.base/share/classes/jdk/internal/vm/VMSupport.java @@ -98,6 +98,11 @@ public class VMSupport { return serializePropertiesToByteArray(onlyStrings(System.getProperties())); } + public static byte[] serializeSecurityPropertiesToByteArray() throws IOException { + Properties p = SharedSecrets.getJavaSecurityPropertiesAccess().getCurrentProperties(); + return serializePropertiesToByteArray(onlyStrings(p)); + } + public static byte[] serializeAgentPropertiesToByteArray() throws IOException { return serializePropertiesToByteArray(onlyStrings(getAgentProperties())); } diff --git a/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java b/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java index 3a915cf96df..480553e9a62 100644 --- a/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java +++ b/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java @@ -1924,9 +1924,15 @@ public class HttpURLConnection extends java.net.HttpURLConnection { } statusLine = responses.getValue(0); - StringTokenizer st = new StringTokenizer(statusLine); - st.nextToken(); - respCode = Integer.parseInt(st.nextToken().trim()); + respCode = parseConnectResponseCode(statusLine); + if (respCode == -1) { + // a respCode of -1, due to a invalid status line, + // will (rightly) result in an IOException being thrown + // later in this code. here we merely log the invalid status line. + if (logger.isLoggable(PlatformLogger.Level.FINE)) { + logger.fine("invalid status line: \"" + statusLine + "\""); + } + } if (respCode == HTTP_PROXY_AUTH) { // Read comments labeled "Failed Negotiate" for details. boolean dontUseNegotiate = false; @@ -2027,6 +2033,37 @@ public class HttpURLConnection extends java.net.HttpURLConnection { responses.reset(); } + // parses the status line, that was returned for a CONNECT request, and returns + // the response code from that line. returns -1 if the response code could not be + // parsed. + private static int parseConnectResponseCode(final String statusLine) { + final int invalidStatusLine = -1; + if (statusLine == null || statusLine.isBlank()) { + return invalidStatusLine; + } + // + // status-line = HTTP-version SP status-code SP [ reason-phrase ] + // SP = space character + // + final StringTokenizer st = new StringTokenizer(statusLine, " "); + if (!st.hasMoreTokens()) { + return invalidStatusLine; + } + st.nextToken(); // the HTTP version part (ex: HTTP/1.1) + if (!st.hasMoreTokens()) { + return invalidStatusLine; + } + final String v = st.nextToken().trim(); // status code + try { + return Integer.parseInt(v); + } catch (NumberFormatException nfe) { + if (logger.isLoggable(PlatformLogger.Level.FINE)) { + logger.fine("invalid response code: " + v); + } + } + return invalidStatusLine; + } + /** * Overridden in https to also include the server certificate */ diff --git a/src/java.base/share/classes/sun/nio/cs/UTF_32Coder.java b/src/java.base/share/classes/sun/nio/cs/UTF_32Coder.java index c6f38ec9bfc..72e59d22e2c 100644 --- a/src/java.base/share/classes/sun/nio/cs/UTF_32Coder.java +++ b/src/java.base/share/classes/sun/nio/cs/UTF_32Coder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, 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 @@ -185,5 +185,12 @@ class UTF_32Coder { doneBOM = !doBOM; } + public boolean canEncode(char c) { + return !Character.isSurrogate(c); + } + + public boolean canEncode(CharSequence cs) { + return Unicode.isValidUnicode(cs); + } } } diff --git a/src/java.base/share/classes/sun/nio/cs/UTF_8.java b/src/java.base/share/classes/sun/nio/cs/UTF_8.java index 2928ae6d509..fda8e5eec1f 100644 --- a/src/java.base/share/classes/sun/nio/cs/UTF_8.java +++ b/src/java.base/share/classes/sun/nio/cs/UTF_8.java @@ -424,6 +424,10 @@ public final class UTF_8 extends Unicode { return !Character.isSurrogate(c); } + public boolean canEncode(CharSequence cs) { + return Unicode.isValidUnicode(cs); + } + public boolean isLegalReplacement(byte[] repl) { return ((repl.length == 1 && repl[0] >= 0) || super.isLegalReplacement(repl)); diff --git a/src/java.base/share/classes/sun/nio/cs/Unicode.java b/src/java.base/share/classes/sun/nio/cs/Unicode.java index aac77a13ffb..06a50f125c5 100644 --- a/src/java.base/share/classes/sun/nio/cs/Unicode.java +++ b/src/java.base/share/classes/sun/nio/cs/Unicode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, 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 @@ -95,4 +95,23 @@ abstract class Unicode extends Charset || (cs.name().equals("x-Johab")) || (cs.name().equals("Shift_JIS"))); } + + static boolean isValidUnicode(CharSequence cs) { + int length = cs.length(); + for (int i = 0; i < length;) { + char c = cs.charAt(i++); + if (Character.isHighSurrogate(c)) { + if (i == length) { + return false; + } + char low = cs.charAt(i++); + if (!Character.isLowSurrogate(low)) { + return false; + } + } else if (Character.isLowSurrogate(c)) { + return false; + } + } + return true; + } } diff --git a/src/java.base/share/classes/sun/nio/cs/UnicodeEncoder.java b/src/java.base/share/classes/sun/nio/cs/UnicodeEncoder.java index 7b34fb2d512..6f7413dcbf8 100644 --- a/src/java.base/share/classes/sun/nio/cs/UnicodeEncoder.java +++ b/src/java.base/share/classes/sun/nio/cs/UnicodeEncoder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -108,4 +108,8 @@ public abstract class UnicodeEncoder extends CharsetEncoder { public boolean canEncode(char c) { return ! Character.isSurrogate(c); } + + public boolean canEncode(CharSequence cs) { + return Unicode.isValidUnicode(cs); + } } diff --git a/src/java.base/share/classes/sun/security/provider/DigestBase.java b/src/java.base/share/classes/sun/security/provider/DigestBase.java index 2aaf0a2fac6..0bb15ef3efe 100644 --- a/src/java.base/share/classes/sun/security/provider/DigestBase.java +++ b/src/java.base/share/classes/sun/security/provider/DigestBase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -242,4 +242,21 @@ abstract class DigestBase extends MessageDigestSpi implements Cloneable { padding = new byte[136]; padding[0] = (byte)0x80; } + + /** + * Digest block-length bytes in a single operation. + * Subclasses are expected to override this method. It is intended + * for fixed-length short input where input includes padding bytes. + * @param input byte array to be digested + * @param inLen the length of the input + * @param output the output buffer + * @param outOffset the offset into output buffer where digest should be written + * @param outLen the length of the output buffer + * @throws UnsupportedOperationException if a subclass does not override this method + */ + void implDigestFixedLengthPreprocessed ( + byte[] input, int inLen, byte[] output, int outOffset, int outLen) + throws UnsupportedOperationException { + throw new UnsupportedOperationException("should not be here"); + } } diff --git a/src/java.base/share/classes/sun/security/provider/HSS.java b/src/java.base/share/classes/sun/security/provider/HSS.java index c1cb5ed6a30..50afba7cab8 100644 --- a/src/java.base/share/classes/sun/security/provider/HSS.java +++ b/src/java.base/share/classes/sun/security/provider/HSS.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, 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 @@ -24,16 +24,22 @@ */ package sun.security.provider; +import java.io.ByteArrayOutputStream; +import java.io.InvalidObjectException; +import java.io.Serial; +import java.io.Serializable; +import java.security.SecureRandom; +import java.security.*; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.Arrays; + import sun.security.util.*; import sun.security.x509.AlgorithmId; import sun.security.x509.X509Key; -import java.io.*; -import java.security.*; -import java.security.SecureRandom; -import java.security.spec.*; -import java.util.Arrays; - /** * Implementation of the Hierarchical Signature System using the * Leighton-Micali Signatures (HSS/LMS) as described in RFC 8554 and @@ -196,42 +202,94 @@ public final class HSS extends SignatureSpi { static class LMSUtils { static final int LMS_RESERVED = 0; - static final int LMS_SHA256_M32_H5 = 5; - static final int LMS_SHA256_M32_H10 = 6; - static final int LMS_SHA256_M32_H15 = 7; - static final int LMS_SHA256_M32_H20 = 8; - static final int LMS_SHA256_M32_H25 = 9; + static final int LMS_SHA256_M32_H5 = 0x05; + static final int LMS_SHA256_M32_H10 = 0x06; + static final int LMS_SHA256_M32_H15 = 0x07; + static final int LMS_SHA256_M32_H20 = 0x08; + static final int LMS_SHA256_M32_H25 = 0x09; + static final int LMS_SHA256_M24_H5 = 0x0a; + static final int LMS_SHA256_M24_H10 = 0x0b; + static final int LMS_SHA256_M24_H15 = 0x0c; + static final int LMS_SHA256_M24_H20 = 0x0d; + static final int LMS_SHA256_M24_H25 = 0x0e; + static final int LMS_SHAKE_M32_H5 = 0x0f; + static final int LMS_SHAKE_M32_H10 = 0x10; + static final int LMS_SHAKE_M32_H15 = 0x11; + static final int LMS_SHAKE_M32_H20 = 0x12; + static final int LMS_SHAKE_M32_H25 = 0x13; + static final int LMS_SHAKE_M24_H5 = 0x14; + static final int LMS_SHAKE_M24_H10 = 0x15; + static final int LMS_SHAKE_M24_H15 = 0x16; + static final int LMS_SHAKE_M24_H20 = 0x17; + static final int LMS_SHAKE_M24_H25 = 0x18; static String lmsType(int type) { - String typeStr; - switch (type) { - case LMS_RESERVED: typeStr = "LMS_RESERVED"; break; - case LMS_SHA256_M32_H5: typeStr = "LMS_SHA256_M32_H5"; break; - case LMS_SHA256_M32_H10: typeStr = "LMS_SHA256_M32_H10"; break; - case LMS_SHA256_M32_H15: typeStr = "LMS_SHA256_M32_H15"; break; - case LMS_SHA256_M32_H20: typeStr = "LMS_SHA256_M32_H20"; break; - case LMS_SHA256_M32_H25: typeStr = "LMS_SHA256_M32_H25"; break; - default: typeStr = "unrecognized"; - } + String typeStr = switch (type) { + case LMS_RESERVED -> "LMS_RESERVED"; + case LMS_SHA256_M32_H5 -> "LMS_SHA256_M32_H5"; + case LMS_SHA256_M32_H10 -> "LMS_SHA256_M32_H10"; + case LMS_SHA256_M32_H15 -> "LMS_SHA256_M32_H15"; + case LMS_SHA256_M32_H20 -> "LMS_SHA256_M32_H20"; + case LMS_SHA256_M32_H25 -> "LMS_SHA256_M32_H25"; + case LMS_SHA256_M24_H5 -> "LMS_SHA256_M24_H5"; + case LMS_SHA256_M24_H10 -> "LMS_SHA256_M24_H10"; + case LMS_SHA256_M24_H15 -> "LMS_SHA256_M24_H15"; + case LMS_SHA256_M24_H20 -> "LMS_SHA256_M24_H20"; + case LMS_SHA256_M24_H25 -> "LMS_SHA256_M24_H25"; + case LMS_SHAKE_M32_H5 -> "LMS_SHAKE_M32_H5"; + case LMS_SHAKE_M32_H10 -> "LMS_SHAKE_M32_H10"; + case LMS_SHAKE_M32_H15 -> "LMS_SHAKE_M32_H15"; + case LMS_SHAKE_M32_H20 -> "LMS_SHAKE_M32_H20"; + case LMS_SHAKE_M32_H25 -> "LMS_SHAKE_M32_H25"; + case LMS_SHAKE_M24_H5 -> "LMS_SHAKE_M24_H5"; + case LMS_SHAKE_M24_H10 -> "LMS_SHAKE_M24_H10"; + case LMS_SHAKE_M24_H15 -> "LMS_SHAKE_M24_H15"; + case LMS_SHAKE_M24_H20 -> "LMS_SHAKE_M24_H20"; + case LMS_SHAKE_M24_H25 -> "LMS_SHAKE_M24_H25"; + default -> "unrecognized"; + }; return typeStr; } static final int LMOTS_RESERVED = 0; - static final int LMOTS_SHA256_N32_W1 = 1; - static final int LMOTS_SHA256_N32_W2 = 2; - static final int LMOTS_SHA256_N32_W4 = 3; - static final int LMOTS_SHA256_N32_W8 = 4; + static final int LMOTS_SHA256_N32_W1 = 0x01; + static final int LMOTS_SHA256_N32_W2 = 0x02; + static final int LMOTS_SHA256_N32_W4 = 0x03; + static final int LMOTS_SHA256_N32_W8 = 0x04; + static final int LMOTS_SHA256_N24_W1 = 0x05; + static final int LMOTS_SHA256_N24_W2 = 0x06; + static final int LMOTS_SHA256_N24_W4 = 0x07; + static final int LMOTS_SHA256_N24_W8 = 0x08; + static final int LMOTS_SHAKE_N32_W1 = 0x09; + static final int LMOTS_SHAKE_N32_W2 = 0x0a; + static final int LMOTS_SHAKE_N32_W4 = 0x0b; + static final int LMOTS_SHAKE_N32_W8 = 0x0c; + static final int LMOTS_SHAKE_N24_W1 = 0x0d; + static final int LMOTS_SHAKE_N24_W2 = 0x0e; + static final int LMOTS_SHAKE_N24_W4 = 0x0f; + static final int LMOTS_SHAKE_N24_W8 = 0x10; static String lmotsType(int type) { - String typeStr; - switch (type) { - case LMOTS_RESERVED: typeStr = "LMOTS_RESERVED"; break; - case LMOTS_SHA256_N32_W1: typeStr = "LMOTS_SHA256_N32_W1"; break; - case LMOTS_SHA256_N32_W2: typeStr = "LMOTS_SHA256_N32_W2"; break; - case LMOTS_SHA256_N32_W4: typeStr = "LMOTS_SHA256_N32_W4"; break; - case LMOTS_SHA256_N32_W8: typeStr = "LMOTS_SHA256_N32_W8"; break; - default: typeStr = "unrecognized"; - } + String typeStr = switch (type) { + case LMOTS_RESERVED -> "LMOTS_RESERVED"; + case LMOTS_SHA256_N32_W1 -> "LMOTS_SHA256_N32_W1"; + case LMOTS_SHA256_N32_W2 -> "LMOTS_SHA256_N32_W2"; + case LMOTS_SHA256_N32_W4 -> "LMOTS_SHA256_N32_W4"; + case LMOTS_SHA256_N32_W8 -> "LMOTS_SHA256_N32_W8"; + case LMOTS_SHA256_N24_W1 -> "LMOTS_SHA256_N24_W1"; + case LMOTS_SHA256_N24_W2 -> "LMOTS_SHA256_N24_W2"; + case LMOTS_SHA256_N24_W4 -> "LMOTS_SHA256_N24_W4"; + case LMOTS_SHA256_N24_W8 -> "LMOTS_SHA256_N24_W8"; + case LMOTS_SHAKE_N32_W1 -> "LMOTS_SHAKE_N32_W1"; + case LMOTS_SHAKE_N32_W2 -> "LMOTS_SHAKE_N32_W2"; + case LMOTS_SHAKE_N32_W4 -> "LMOTS_SHAKE_N32_W4"; + case LMOTS_SHAKE_N32_W8 -> "LMOTS_SHAKE_N32_W8"; + case LMOTS_SHAKE_N24_W1 -> "LMOTS_SHAKE_N24_W1"; + case LMOTS_SHAKE_N24_W2 -> "LMOTS_SHAKE_N24_W2"; + case LMOTS_SHAKE_N24_W4 -> "LMOTS_SHAKE_N24_W4"; + case LMOTS_SHAKE_N24_W8 -> "LMOTS_SHAKE_N24_W8"; + default -> "unrecognized"; + }; return typeStr; } @@ -352,53 +410,65 @@ public final class HSS extends SignatureSpi { static class LMSParams { final int m; // the number of bytes used from the hash output - final int hashAlg_m = 32; // output length of the LMS tree hash function + final int hashAlg_m; // output length of the LMS tree hash function final int h; // height of the LMS tree final int twoPowh; final String hashAlgStr; - LMSParams(int m, int h, String hashAlgStr) { + private LMSParams(int m, int h, String hashAlgStr, int hashAlg_m) { this.m = m; this.h = h; this.hashAlgStr = hashAlgStr; + this.hashAlg_m = hashAlg_m; twoPowh = 1 << h; } static LMSParams of(int type) { - int m; - int h; - String hashAlgStr; - switch (type) { - case LMSUtils.LMS_SHA256_M32_H5: - m = 32; - h = 5; - hashAlgStr = "SHA-256"; - break; - case LMSUtils.LMS_SHA256_M32_H10: - m = 32; - h = 10; - hashAlgStr = "SHA-256"; - break; - case LMSUtils.LMS_SHA256_M32_H15: - m = 32; - h = 15; - hashAlgStr = "SHA-256"; - break; - case LMSUtils.LMS_SHA256_M32_H20: - m = 32; - h = 20; - hashAlgStr = "SHA-256"; - break; - case LMSUtils.LMS_SHA256_M32_H25: - m = 32; - h = 25; - hashAlgStr = "SHA-256"; - break; - default: + LMSParams params = switch (type) { + case LMSUtils.LMS_SHA256_M32_H5 -> + new LMSParams(32, 5, "SHA-256", 32); + case LMSUtils.LMS_SHA256_M32_H10 -> + new LMSParams(32, 10, "SHA-256", 32); + case LMSUtils.LMS_SHA256_M32_H15 -> + new LMSParams(32, 15, "SHA-256", 32); + case LMSUtils.LMS_SHA256_M32_H20 -> + new LMSParams(32, 20, "SHA-256", 32); + case LMSUtils.LMS_SHA256_M32_H25 -> + new LMSParams(32, 25, "SHA-256", 32); + case LMSUtils.LMS_SHA256_M24_H5 -> + new LMSParams(24, 5, "SHA-256", 32); + case LMSUtils.LMS_SHA256_M24_H10 -> + new LMSParams(24, 10, "SHA-256", 32); + case LMSUtils.LMS_SHA256_M24_H15 -> + new LMSParams(24, 15, "SHA-256", 32); + case LMSUtils.LMS_SHA256_M24_H20 -> + new LMSParams(24, 20, "SHA-256", 32); + case LMSUtils.LMS_SHA256_M24_H25 -> + new LMSParams(24, 25, "SHA-256", 32); + case LMSUtils.LMS_SHAKE_M32_H5 -> + new LMSParams(32, 5, "SHAKE256-512", 64); + case LMSUtils.LMS_SHAKE_M32_H10 -> + new LMSParams(32, 10, "SHAKE256-512", 64); + case LMSUtils.LMS_SHAKE_M32_H15 -> + new LMSParams(32, 15, "SHAKE256-512", 64); + case LMSUtils.LMS_SHAKE_M32_H20 -> + new LMSParams(32, 20, "SHAKE256-512", 64); + case LMSUtils.LMS_SHAKE_M32_H25 -> + new LMSParams(32, 25, "SHAKE256-512", 64); + case LMSUtils.LMS_SHAKE_M24_H5 -> + new LMSParams(24, 5, "SHAKE256-512", 64); + case LMSUtils.LMS_SHAKE_M24_H10 -> + new LMSParams(24, 10, "SHAKE256-512", 64); + case LMSUtils.LMS_SHAKE_M24_H15 -> + new LMSParams(24, 15, "SHAKE256-512", 64); + case LMSUtils.LMS_SHAKE_M24_H20 -> + new LMSParams(24, 20, "SHAKE256-512", 64); + case LMSUtils.LMS_SHAKE_M24_H25 -> + new LMSParams(24, 25, "SHAKE256-512", 64); + default -> throw new IllegalArgumentException("Unsupported or bad LMS type"); - } - - return new LMSParams(m, h, hashAlgStr); + }; + return params; } boolean hasSameHash(LMSParams other) { @@ -495,7 +565,7 @@ public final class HSS extends SignatureSpi { static class LMOTSParams { final int lmotSigType; final int n; // the number of bytes used from the hash output - final int hashAlg_n = 32; // the output length of the hash function + int hashAlg_n; // the output length of the hash function final int w; final int twoPowWMinus1; final int ls; @@ -511,6 +581,7 @@ public final class HSS extends SignatureSpi { // back into the buffer. This way, we avoid memory allocations and some // computations that would have to be done otherwise. final byte[] hashBuf; + // Precomputed block for SHA256 when the message size is 55 bytes // (i.e. when SHA256 is used) private static final byte[] hashbufSha256_32 = { @@ -523,10 +594,64 @@ public final class HSS extends SignatureSpi { 0, 0, 0, 0, 0, 0, 0, (byte) 0x80, 0, 0, 0, 0, 0, 0, 1, (byte) 0xb8 }; + // Precomputed block for SHA256 when the message size is 47 bytes + // (i.e. when SHA256-192 is used) + private static final byte[] hashbufSha256_24 = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, (byte) 0x80, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 0x78 + }; + // Precomputed block for SHAKE256 when the message size is 55 bytes + // (i.e. when SHAKE256 is used) + private static final byte[] hashbufShake256_32 = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, (byte) 0x1F, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, (byte) 0x80 + }; + // Precomputed block for SHAKE256 when the message size is 47 bytes + // (i.e. when SHAKE256-192 is used) + private static final byte[] hashbufShake256_24 = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, (byte) 0x1F, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, (byte) 0x80 + }; private LMOTSParams( int lmotSigType, int hLen, int w, - int ls, int p, String hashAlgName) { + int ls, int p, String hashAlgName, int hashAlg_n) { this.lmotSigType = lmotSigType; this.n = hLen; this.w = w; @@ -534,32 +659,60 @@ public final class HSS extends SignatureSpi { this.p = p; twoPowWMinus1 = (1 << w) - 1; this.hashAlgName = hashAlgName; - hashBuf = hashbufSha256_32; + this.hashAlg_n = hashAlg_n; + hashBuf = switch (hashAlgName) { + case "SHAKE256-512" -> { + yield this.n == 24 ? + hashbufShake256_24 : hashbufShake256_32; + } + case "SHA-256" -> { + yield this.n == 24 ? + hashbufSha256_24 : hashbufSha256_32; + } + default -> + throw new IllegalArgumentException( + "Unknown hash algorithm "+hashAlgName); + }; } static LMOTSParams of(int lmotsType) { - LMOTSParams params; - switch (lmotsType) { - case LMSUtils.LMOTS_SHA256_N32_W1: - params = new LMOTSParams( - lmotsType, 32, 1, 7, 265, "SHA-256"); - break; - case LMSUtils.LMOTS_SHA256_N32_W2: - params = new LMOTSParams( - lmotsType, 32, 2, 6, 133, "SHA-256"); - break; - case LMSUtils.LMOTS_SHA256_N32_W4: - params = new LMOTSParams( - lmotsType, 32, 4, 4, 67, "SHA-256"); - break; - case LMSUtils.LMOTS_SHA256_N32_W8: - params = new LMOTSParams( - lmotsType, 32, 8, 0, 34, "SHA-256"); - break; - default: + LMOTSParams params = switch (lmotsType) { + case LMSUtils.LMOTS_SHA256_N32_W1 -> + new LMOTSParams(lmotsType, 32, 1, 7, 265, "SHA-256", 32); + case LMSUtils.LMOTS_SHA256_N32_W2 -> + new LMOTSParams(lmotsType, 32, 2, 6, 133, "SHA-256", 32); + case LMSUtils.LMOTS_SHA256_N32_W4 -> + new LMOTSParams(lmotsType, 32, 4, 4, 67, "SHA-256", 32); + case LMSUtils.LMOTS_SHA256_N32_W8 -> + new LMOTSParams(lmotsType, 32, 8, 0, 34, "SHA-256", 32); + case LMSUtils.LMOTS_SHA256_N24_W1 -> + new LMOTSParams(lmotsType, 24, 1, 8, 200, "SHA-256", 32); + case LMSUtils.LMOTS_SHA256_N24_W2 -> + new LMOTSParams(lmotsType, 24, 2, 6, 101, "SHA-256", 32); + case LMSUtils.LMOTS_SHA256_N24_W4 -> + new LMOTSParams(lmotsType, 24, 4, 4, 51, "SHA-256", 32); + case LMSUtils.LMOTS_SHA256_N24_W8 -> + new LMOTSParams(lmotsType, 24, 8, 0, 26, "SHA-256", 32); + case LMSUtils.LMOTS_SHAKE_N32_W1 -> + new LMOTSParams(lmotsType, 32, 1, 7, 265, "SHAKE256-512", 64); + case LMSUtils.LMOTS_SHAKE_N32_W2 -> + new LMOTSParams(lmotsType, 32, 2, 6, 133, "SHAKE256-512", 64); + case LMSUtils.LMOTS_SHAKE_N32_W4 -> + new LMOTSParams(lmotsType, 32, 4, 4, 67, "SHAKE256-512", 64); + case LMSUtils.LMOTS_SHAKE_N32_W8 -> + new LMOTSParams(lmotsType, 32, 8, 0, 34, "SHAKE256-512", 64); + case LMSUtils.LMOTS_SHAKE_N24_W1 -> + new LMOTSParams(lmotsType, 24, 1, 8, 200, "SHAKE256-512", 64); + case LMSUtils.LMOTS_SHAKE_N24_W2 -> + new LMOTSParams(lmotsType, 24, 2, 6, 101, "SHAKE256-512", 64); + case LMSUtils.LMOTS_SHAKE_N24_W4 -> + new LMOTSParams(lmotsType, 24, 4, 4, 51, "SHAKE256-512", 64); + case LMSUtils.LMOTS_SHAKE_N24_W8 -> + new LMOTSParams(lmotsType, 24, 8, 0, 26, "SHAKE256-512", 64); + default -> throw new IllegalArgumentException( "Unsupported or bad OTS Algorithm Identifier."); - } + }; return params; } @@ -580,13 +733,6 @@ public final class HSS extends SignatureSpi { S[len + 1] = (byte) (sum & 0xff); } - void digestFixedLengthPreprocessed( - SHA2.SHA256 sha256, byte[] input, int inLen, - byte[] output, int outOffset, int outLen) { - sha256.implDigestFixedLengthPreprocessed( - input, inLen, output, outOffset, outLen); - } - byte[] lmotsPubKeyCandidate( LMSignature lmSig, byte[] message, LMSPublicKey pKey) throws SignatureException { @@ -625,7 +771,13 @@ public final class HSS extends SignatureSpi { byte[] preZi = hashBuf.clone(); int hashLen = hashBuf.length; - SHA2.SHA256 sha256 = new SHA2.SHA256(); + + DigestBase db; + if (hashAlgName.startsWith("SHAKE")) { + db = new SHA3.SHAKE256Hash(); + } else { + db = new SHA2.SHA256(); + } pKey.getI(preZi, 0); lmSig.getQArr(preZi, 16); @@ -643,11 +795,11 @@ public final class HSS extends SignatureSpi { for (int j = a; j < twoPowWMinus1; j++) { preZi[22] = (byte) j; if (j < twoPowWMinus2) { - digestFixedLengthPreprocessed( - sha256, preZi, hashLen, preZi, 23, n); + db.implDigestFixedLengthPreprocessed(preZi, + hashLen, preZi, 23, n); } else { - digestFixedLengthPreprocessed( - sha256, preZi, hashLen, preCandidate, 22 + i * n, n); + db.implDigestFixedLengthPreprocessed(preZi, + hashLen, preCandidate, 22 + i * n, n); } } } diff --git a/src/java.base/share/classes/sun/security/provider/SHA2.java b/src/java.base/share/classes/sun/security/provider/SHA2.java index e966e6b77f8..7d8c2840de9 100644 --- a/src/java.base/share/classes/sun/security/provider/SHA2.java +++ b/src/java.base/share/classes/sun/security/provider/SHA2.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2026, 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 @@ -117,6 +117,7 @@ abstract class SHA2 extends DigestBase { } + @Override protected void implDigestFixedLengthPreprocessed( byte[] input, int inLen, byte[] output, int outOffset, int outLen) { implReset(); diff --git a/src/java.base/share/classes/sun/security/provider/SHA3.java b/src/java.base/share/classes/sun/security/provider/SHA3.java index a096cac5f50..0578645c1cd 100644 --- a/src/java.base/share/classes/sun/security/provider/SHA3.java +++ b/src/java.base/share/classes/sun/security/provider/SHA3.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, 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 @@ -98,6 +98,15 @@ public abstract class SHA3 extends DigestBase { this.suffix = suffix; } + @Override + protected void implDigestFixedLengthPreprocessed( + byte[] input, int inLen, byte[] output, int outOffset, int outLen) { + implReset(); + + implCompress(input, 0); + implDigest0(output, outOffset, outLen); + } + private void implCompressCheck(byte[] b, int ofs) { Objects.requireNonNull(b); Preconditions.checkIndex(ofs + blockSize - 1, b.length, Preconditions.AIOOBE_FORMATTER); @@ -136,9 +145,6 @@ public abstract class SHA3 extends DigestBase { * DigestBase calls implReset() when necessary. */ void implDigest(byte[] out, int ofs) { - // Moving this allocation to the block where it is used causes a little - // performance drop, that is why it is here. - byte[] byteState = new byte[8]; if (engineGetDigestLength() == 0) { // This is an XOF, so the digest() call is illegal. throw new ProviderException("Calling digest() is not allowed in an XOF"); @@ -146,8 +152,12 @@ public abstract class SHA3 extends DigestBase { finishAbsorb(); + implDigest0(out, ofs, engineGetDigestLength()); + } + + void implDigest0(byte[] out, int ofs, int outLen) { int availableBytes = blockSize; - int numBytes = engineGetDigestLength(); + int numBytes = outLen; while (numBytes > availableBytes) { for (int i = 0; i < availableBytes / 8; i++) { @@ -163,6 +173,10 @@ public abstract class SHA3 extends DigestBase { asLittleEndian.set(out, ofs, state[i]); ofs += 8; } + + // Moving this allocation to the block where it is used causes a little + // performance drop, that is why it is here. + byte[] byteState = new byte[8]; if (numBytes % 8 != 0) { asLittleEndian.set(byteState, 0, state[numLongs]); System.arraycopy(byteState, 0, out, ofs, numBytes % 8); diff --git a/src/java.base/share/classes/sun/security/provider/X509Factory.java b/src/java.base/share/classes/sun/security/provider/X509Factory.java index f732c7c0455..4be83d629bb 100644 --- a/src/java.base/share/classes/sun/security/provider/X509Factory.java +++ b/src/java.base/share/classes/sun/security/provider/X509Factory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, 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,9 +112,10 @@ public class X509Factory extends CertificateFactorySpi { if (cert != null) { return cert; } - cert = new X509CertImpl(encoding); - addToCache(certCache, cert.getEncodedInternal(), cert); - return cert; + // Build outside lock + X509CertImpl newCert = new X509CertImpl(encoding); + byte[] enc = newCert.getEncodedInternal(); + return addIfNotPresent(certCache, enc, newCert); } /** @@ -156,7 +157,7 @@ public class X509Factory extends CertificateFactorySpi { * @throws CertificateException if failures occur while obtaining the DER * encoding for certificate data. */ - public static synchronized X509CertImpl intern(X509Certificate c) + public static X509CertImpl intern(X509Certificate c) throws CertificateException { if (c == null) { return null; @@ -168,18 +169,23 @@ public class X509Factory extends CertificateFactorySpi { } else { encoding = c.getEncoded(); } - X509CertImpl newC = getFromCache(certCache, encoding); - if (newC != null) { - return newC; + // First check under per-cache lock + X509CertImpl cached = getFromCache(certCache, encoding); + if (cached != null) { + return cached; } + + // Build outside lock + X509CertImpl newC; + byte[] enc; if (isImpl) { - newC = (X509CertImpl)c; + newC = (X509CertImpl) c; + enc = encoding; } else { newC = new X509CertImpl(encoding); - encoding = newC.getEncodedInternal(); + enc = newC.getEncodedInternal(); } - addToCache(certCache, encoding, newC); - return newC; + return addIfNotPresent(certCache, enc, newC); } /** @@ -192,7 +198,7 @@ public class X509Factory extends CertificateFactorySpi { * @throws CRLException if failures occur while obtaining the DER * encoding for CRL data. */ - public static synchronized X509CRLImpl intern(X509CRL c) + public static X509CRLImpl intern(X509CRL c) throws CRLException { if (c == null) { return null; @@ -204,39 +210,47 @@ public class X509Factory extends CertificateFactorySpi { } else { encoding = c.getEncoded(); } - X509CRLImpl newC = getFromCache(crlCache, encoding); - if (newC != null) { - return newC; + X509CRLImpl cached = getFromCache(crlCache, encoding); + if (cached != null) { + return cached; } + + X509CRLImpl newC; + byte[] enc; if (isImpl) { - newC = (X509CRLImpl)c; + newC = (X509CRLImpl) c; + enc = encoding; } else { newC = new X509CRLImpl(encoding); - encoding = newC.getEncodedInternal(); + enc = newC.getEncodedInternal(); } - addToCache(crlCache, encoding, newC); - return newC; + return addIfNotPresent(crlCache, enc, newC); } /** * Get the X509CertImpl or X509CRLImpl from the cache. */ - private static synchronized V getFromCache(Cache cache, - byte[] encoding) { - Object key = new Cache.EqualByteArray(encoding); - return cache.get(key); + private static V getFromCache(Cache cache, byte[] encoding) { + return cache.get(new Cache.EqualByteArray(encoding)); } /** * Add the X509CertImpl or X509CRLImpl to the cache. */ - private static synchronized void addToCache(Cache cache, - byte[] encoding, V value) { + private static V addIfNotPresent(Cache cache, byte[] encoding, V value) { if (encoding.length > ENC_MAX_LENGTH) { - return; + return value; } Object key = new Cache.EqualByteArray(encoding); - cache.put(key, value); + // Synchronize only to make the "check + insert" decision atomic. + synchronized (cache) { + V existing = cache.get(key); + if (existing != null) { + return existing; + } + cache.put(key, value); + return value; + } } /** @@ -389,13 +403,14 @@ public class X509Factory extends CertificateFactorySpi { try { byte[] encoding = readOneBlock(is); if (encoding != null) { - X509CRLImpl crl = getFromCache(crlCache, encoding); - if (crl != null) { - return crl; + X509CRLImpl cached = getFromCache(crlCache, encoding); + if (cached != null) { + return cached; } - crl = new X509CRLImpl(encoding); - addToCache(crlCache, crl.getEncodedInternal(), crl); - return crl; + // Build outside lock + X509CRLImpl crl = new X509CRLImpl(encoding); + byte[] enc = crl.getEncodedInternal(); + return addIfNotPresent(crlCache, enc, crl); } else { throw new IOException("Empty input"); } diff --git a/src/java.base/share/classes/sun/security/ssl/Alert.java b/src/java.base/share/classes/sun/security/ssl/Alert.java index fb06b02a5d4..e9588a09b3d 100644 --- a/src/java.base/share/classes/sun/security/ssl/Alert.java +++ b/src/java.base/share/classes/sun/security/ssl/Alert.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -281,6 +281,8 @@ public enum Alert { // consumer so the state machine doesn't expect it. tc.handshakeContext.handshakeConsumers.remove( SSLHandshake.CERTIFICATE.id); + tc.handshakeContext.handshakeConsumers.remove( + SSLHandshake.COMPRESSED_CERTIFICATE.id); tc.handshakeContext.handshakeConsumers.remove( SSLHandshake.CERTIFICATE_VERIFY.id); } diff --git a/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java b/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java index c6897d71aa6..af5007d7899 100644 --- a/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java +++ b/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java @@ -781,14 +781,6 @@ final class CertificateMessage { } } - T13CertificateMessage(HandshakeContext handshakeContext, - byte[] requestContext, List certificates) { - super(handshakeContext); - - this.requestContext = requestContext.clone(); - this.certEntries = certificates; - } - T13CertificateMessage(HandshakeContext handshakeContext, ByteBuffer m) throws IOException { super(handshakeContext); @@ -925,16 +917,26 @@ final class CertificateMessage { HandshakeMessage message) throws IOException { // The producing happens in handshake context only. HandshakeContext hc = (HandshakeContext)context; - if (hc.sslConfig.isClientMode) { - return onProduceCertificate( - (ClientHandshakeContext)context, message); - } else { - return onProduceCertificate( + T13CertificateMessage cm = hc.sslConfig.isClientMode ? + onProduceCertificate( + (ClientHandshakeContext)context, message) : + onProduceCertificate( (ServerHandshakeContext)context, message); + + // Output the handshake message. + if (hc.certDeflater == null) { + cm.write(hc.handshakeOutput); + hc.handshakeOutput.flush(); + } else { + // Replace with CompressedCertificate message + CompressedCertificate.handshakeProducer.produce(hc, cm); } + + // The handshake message has been delivered. + return null; } - private byte[] onProduceCertificate(ServerHandshakeContext shc, + private T13CertificateMessage onProduceCertificate(ServerHandshakeContext shc, HandshakeMessage message) throws IOException { ClientHelloMessage clientHello = (ClientHelloMessage)message; @@ -993,12 +995,7 @@ final class CertificateMessage { SSLLogger.fine("Produced server Certificate message", cm); } - // Output the handshake message. - cm.write(shc.handshakeOutput); - shc.handshakeOutput.flush(); - - // The handshake message has been delivered. - return null; + return cm; } private static SSLPossession choosePossession( @@ -1045,7 +1042,7 @@ final class CertificateMessage { return pos; } - private byte[] onProduceCertificate(ClientHandshakeContext chc, + private T13CertificateMessage onProduceCertificate(ClientHandshakeContext chc, HandshakeMessage message) throws IOException { ClientHelloMessage clientHello = (ClientHelloMessage)message; SSLPossession pos = choosePossession(chc, clientHello); @@ -1091,12 +1088,7 @@ final class CertificateMessage { SSLLogger.fine("Produced client Certificate message", cm); } - // Output the handshake message. - cm.write(chc.handshakeOutput); - chc.handshakeOutput.flush(); - - // The handshake message has been delivered. - return null; + return cm; } } @@ -1116,6 +1108,7 @@ final class CertificateMessage { HandshakeContext hc = (HandshakeContext)context; // clean up this consumer + hc.handshakeConsumers.remove(SSLHandshake.COMPRESSED_CERTIFICATE.id); hc.handshakeConsumers.remove(SSLHandshake.CERTIFICATE.id); // Ensure that the Certificate message has not been sent w/o diff --git a/src/java.base/share/classes/sun/security/ssl/CertificateRequest.java b/src/java.base/share/classes/sun/security/ssl/CertificateRequest.java index 039399560cd..2eceb4d9ebd 100644 --- a/src/java.base/share/classes/sun/security/ssl/CertificateRequest.java +++ b/src/java.base/share/classes/sun/security/ssl/CertificateRequest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -956,6 +956,11 @@ final class CertificateRequest { // update // shc.certRequestContext = crm.requestContext.clone(); + if (shc.certInflaters != null && !shc.certInflaters.isEmpty()) { + shc.handshakeConsumers.put( + SSLHandshake.COMPRESSED_CERTIFICATE.id, + SSLHandshake.COMPRESSED_CERTIFICATE); + } shc.handshakeConsumers.put(SSLHandshake.CERTIFICATE.id, SSLHandshake.CERTIFICATE); shc.handshakeConsumers.put(SSLHandshake.CERTIFICATE_VERIFY.id, diff --git a/src/java.base/share/classes/sun/security/ssl/CompressCertExtension.java b/src/java.base/share/classes/sun/security/ssl/CompressCertExtension.java new file mode 100644 index 00000000000..eff97857ef0 --- /dev/null +++ b/src/java.base/share/classes/sun/security/ssl/CompressCertExtension.java @@ -0,0 +1,306 @@ +/* + * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (c) 2026, 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 sun.security.ssl; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.text.MessageFormat; +import java.util.Locale; +import java.util.Map; +import java.util.function.Function; +import javax.net.ssl.SSLProtocolException; +import sun.security.ssl.SSLExtension.ExtensionConsumer; +import sun.security.ssl.SSLExtension.SSLExtensionSpec; +import sun.security.ssl.SSLHandshake.HandshakeMessage; + +/** + * Pack of the "compress_certificate" extensions [RFC 5246]. + */ +final class CompressCertExtension { + + static final HandshakeProducer chNetworkProducer = + new CHCompressCertificateProducer(); + static final ExtensionConsumer chOnLoadConsumer = + new CHCompressCertificateConsumer(); + + static final HandshakeProducer crNetworkProducer = + new CRCompressCertificateProducer(); + static final ExtensionConsumer crOnLoadConsumer = + new CRCompressCertificateConsumer(); + + static final SSLStringizer ccStringizer = + new CompressCertificateStringizer(); + + /** + * The "signature_algorithms" extension. + */ + static final class CertCompressionSpec implements SSLExtensionSpec { + + private final int[] compressionAlgorithms; // non-null + + CertCompressionSpec( + Map> certInflaters) { + compressionAlgorithms = new int[certInflaters.size()]; + int i = 0; + for (Integer id : certInflaters.keySet()) { + compressionAlgorithms[i++] = id; + } + } + + CertCompressionSpec(HandshakeContext hc, + ByteBuffer buffer) throws IOException { + if (buffer.remaining() < 2) { // 2: the length of the list + throw hc.conContext.fatal(Alert.DECODE_ERROR, + new SSLProtocolException( + "Invalid compress_certificate: insufficient data")); + } + + byte[] algs = Record.getBytes8(buffer); + if (buffer.hasRemaining()) { + throw hc.conContext.fatal(Alert.DECODE_ERROR, + new SSLProtocolException( + "Invalid compress_certificate: unknown extra data")); + } + + if (algs.length == 0 || (algs.length & 0x01) != 0) { + throw hc.conContext.fatal(Alert.DECODE_ERROR, + new SSLProtocolException( + "Invalid compress_certificate: incomplete data")); + } + + int[] compressionAlgs = new int[algs.length / 2]; + for (int i = 0, j = 0; i < algs.length; ) { + byte hash = algs[i++]; + byte sign = algs[i++]; + compressionAlgs[j++] = ((hash & 0xFF) << 8) | (sign & 0xFF); + } + + this.compressionAlgorithms = compressionAlgs; + } + + @Override + public String toString() { + MessageFormat messageFormat = new MessageFormat( + "\"compression algorithms\": '['{0}']'", Locale.ENGLISH); + + if (compressionAlgorithms.length == 0) { + Object[] messageFields = { + "" + }; + return messageFormat.format(messageFields); + } else { + StringBuilder builder = new StringBuilder(512); + boolean isFirst = true; + for (int ca : compressionAlgorithms) { + if (isFirst) { + isFirst = false; + } else { + builder.append(", "); + } + + builder.append(CompressionAlgorithm.nameOf(ca)); + } + + Object[] messageFields = { + builder.toString() + }; + + return messageFormat.format(messageFields); + } + } + } + + private static final + class CompressCertificateStringizer implements SSLStringizer { + + @Override + public String toString(HandshakeContext hc, ByteBuffer buffer) { + try { + return (new CertCompressionSpec(hc, buffer)).toString(); + } catch (IOException ioe) { + // For debug logging only, so please swallow exceptions. + return ioe.getMessage(); + } + } + } + + /** + * Network data producer of a "compress_certificate" extension in + * the ClientHello handshake message. + */ + private static final + class CHCompressCertificateProducer implements HandshakeProducer { + + // Prevent instantiation of this class. + private CHCompressCertificateProducer() { + // blank + } + + @Override + public byte[] produce(ConnectionContext context, + HandshakeMessage message) throws IOException { + // The producing happens in client side only. + return produceCompCertExt(context, + SSLExtension.CH_COMPRESS_CERTIFICATE); + } + } + + /** + * Network data consumer of a "compress_certificate" extension in + * the ClientHello handshake message. + */ + private static final + class CHCompressCertificateConsumer implements ExtensionConsumer { + + // Prevent instantiation of this class. + private CHCompressCertificateConsumer() { + // blank + } + + @Override + public void consume(ConnectionContext context, + HandshakeMessage message, ByteBuffer buffer) + throws IOException { + // The consuming happens in server side only. + consumeCompCertExt(context, buffer, + SSLExtension.CH_COMPRESS_CERTIFICATE); + } + } + + /** + * Network data producer of a "compress_certificate" extension in + * the CertificateRequest handshake message. + */ + private static final + class CRCompressCertificateProducer implements HandshakeProducer { + + // Prevent instantiation of this class. + private CRCompressCertificateProducer() { + // blank + } + + @Override + public byte[] produce(ConnectionContext context, + HandshakeMessage message) throws IOException { + // The producing happens in server side only. + return produceCompCertExt(context, + SSLExtension.CR_COMPRESS_CERTIFICATE); + } + } + + /** + * Network data consumer of a "compress_certificate" extension in + * the CertificateRequest handshake message. + */ + private static final + class CRCompressCertificateConsumer implements ExtensionConsumer { + + // Prevent instantiation of this class. + private CRCompressCertificateConsumer() { + // blank + } + + @Override + public void consume(ConnectionContext context, + HandshakeMessage message, ByteBuffer buffer) + throws IOException { + // The consuming happens in client side only. + consumeCompCertExt(context, buffer, + SSLExtension.CR_COMPRESS_CERTIFICATE); + } + } + + private static byte[] produceCompCertExt( + ConnectionContext context, SSLExtension extension) + throws IOException { + + HandshakeContext hc = (HandshakeContext) context; + // Is it a supported and enabled extension? + if (!hc.sslConfig.isAvailable(extension)) { + if (SSLLogger.isOn() && SSLLogger.isOn(SSLLogger.Opt.HANDSHAKE)) { + SSLLogger.fine("Ignore unavailable " + + "compress_certificate extension"); + } + return null; + } + + // Produce the extension. + hc.certInflaters = CompressionAlgorithm.getInflaters(); + + if (hc.certInflaters.isEmpty()) { + if (SSLLogger.isOn() && SSLLogger.isOn(SSLLogger.Opt.HANDSHAKE)) { + SSLLogger.warning("Unable to produce the extension: " + + "no certificate compression inflaters defined"); + } + return null; + } + + int vectorLen = CompressionAlgorithm.sizeInRecord() * + hc.certInflaters.size(); + byte[] extData = new byte[vectorLen + 1]; + ByteBuffer m = ByteBuffer.wrap(extData); + Record.putInt8(m, vectorLen); + for (Integer algId : hc.certInflaters.keySet()) { + Record.putInt16(m, algId); + } + + // Update the context. + hc.handshakeExtensions.put( + extension, new CertCompressionSpec(hc.certInflaters)); + + return extData; + } + + private static void consumeCompCertExt(ConnectionContext context, + ByteBuffer buffer, SSLExtension extension) throws IOException { + + HandshakeContext hc = (HandshakeContext) context; + // Is it a supported and enabled extension? + if (!hc.sslConfig.isAvailable(extension)) { + if (SSLLogger.isOn() && SSLLogger.isOn(SSLLogger.Opt.HANDSHAKE)) { + SSLLogger.fine("Ignore unavailable " + + "compress_certificate extension"); + } + return; // ignore the extension + } + + // Parse the extension. + CertCompressionSpec spec = new CertCompressionSpec(hc, buffer); + + // Update the context. + hc.certDeflater = CompressionAlgorithm.selectDeflater( + spec.compressionAlgorithms); + + if (hc.certDeflater == null) { + if (SSLLogger.isOn() && SSLLogger.isOn(SSLLogger.Opt.HANDSHAKE)) { + SSLLogger.fine("Ignore, no supported " + + "certificate compression algorithms"); + } + } + // No impact on session resumption. + } +} diff --git a/src/java.base/share/classes/sun/security/ssl/CompressedCertificate.java b/src/java.base/share/classes/sun/security/ssl/CompressedCertificate.java new file mode 100644 index 00000000000..067a0344c9d --- /dev/null +++ b/src/java.base/share/classes/sun/security/ssl/CompressedCertificate.java @@ -0,0 +1,264 @@ +/* + * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (c) 2026, 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 sun.security.ssl; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.text.MessageFormat; +import java.util.Locale; +import java.util.function.Function; +import javax.net.ssl.SSLProtocolException; +import sun.security.ssl.SSLHandshake.HandshakeMessage; +import sun.security.util.Cache; +import sun.security.util.Cache.EqualByteArray; +import sun.security.util.HexDumpEncoder; + +/** + * Pack of the CompressedCertificate handshake message. + */ +final class CompressedCertificate { + + static final SSLConsumer handshakeConsumer = + new CompressedCertConsumer(); + static final HandshakeProducer handshakeProducer = + new CompressedCertProducer(); + + record CompCertCacheKey(EqualByteArray eba, int algId) {} + + /** + * The CompressedCertificate handshake message for TLS 1.3. + */ + static final class CompressedCertMessage extends HandshakeMessage { + + private final int algorithmId; + private final int uncompressedLength; + private final byte[] compressedCert; + + CompressedCertMessage(HandshakeContext context, + int algorithmId, int uncompressedLength, + byte[] compressedCert) { + super(context); + + this.algorithmId = algorithmId; + this.uncompressedLength = uncompressedLength; + this.compressedCert = compressedCert; + } + + CompressedCertMessage(HandshakeContext handshakeContext, + ByteBuffer m) throws IOException { + super(handshakeContext); + + // struct { + // CertificateCompressionAlgorithm algorithm; + // uint24 uncompressed_length; + // opaque compressed_certificate_message<1..2^24-1>; + // } CompressedCertificate; + if (m.remaining() < 9) { + throw new SSLProtocolException( + "Invalid CompressedCertificate message: " + + "insufficient data (length=" + m.remaining() + + ")"); + } + this.algorithmId = Record.getInt16(m); + this.uncompressedLength = Record.getInt24(m); + this.compressedCert = Record.getBytes24(m); + + if (m.hasRemaining()) { + throw handshakeContext.conContext.fatal( + Alert.HANDSHAKE_FAILURE, + "Invalid CompressedCertificate message: " + + "unknown extra data"); + } + } + + @Override + public SSLHandshake handshakeType() { + return SSLHandshake.COMPRESSED_CERTIFICATE; + } + + @Override + public int messageLength() { + return 8 + compressedCert.length; + } + + @Override + public void send(HandshakeOutStream hos) throws IOException { + hos.putInt16(algorithmId); + hos.putInt24(uncompressedLength); + hos.putBytes24(compressedCert); + } + + @Override + public String toString() { + MessageFormat messageFormat = new MessageFormat( + """ + "CompressedCertificate": '{' + "algorithm": "{0}", + "uncompressed_length": {1} + "compressed_certificate_message": [ + {2} + ] + '}'""", + Locale.ENGLISH); + + HexDumpEncoder hexEncoder = new HexDumpEncoder(); + Object[] messageFields = { + CompressionAlgorithm.nameOf(algorithmId), + uncompressedLength, + Utilities.indent(hexEncoder.encode(compressedCert), " ") + }; + + return messageFormat.format(messageFields); + } + } + + /** + * The "CompressedCertificate" handshake message producer for TLS 1.3. + */ + private static final + class CompressedCertProducer implements HandshakeProducer { + + // Prevent instantiation of this class. + private CompressedCertProducer() { + // blank + } + + // Note this is a special producer, which can only be called from + // the CertificateMessage producer. The input 'message' parameter + // represents the Certificate handshake message. + @Override + public byte[] produce(ConnectionContext context, + HandshakeMessage message) throws IOException { + // The producing happens in handshake context only. + HandshakeContext hc = (HandshakeContext) context; + + // Compress the Certificate message. + HandshakeOutStream hos = new HandshakeOutStream(null); + message.send(hos); + byte[] certMsg = hos.toByteArray(); + byte[] compressedCertMsg; + + // First byte is the size of certificate_request_context which + // should be random if present. Don't cache a randomized message. + if (certMsg[0] != 0) { + compressedCertMsg = hc.certDeflater.getValue().apply(certMsg); + } else { + Cache cache = + hc.sslContext.getCompCertCache(); + CompCertCacheKey key = new CompCertCacheKey( + new EqualByteArray(certMsg), hc.certDeflater.getKey()); + compressedCertMsg = cache.get(key); + + if (compressedCertMsg == null) { + compressedCertMsg = + hc.certDeflater.getValue().apply(certMsg); + + if (SSLLogger.isOn() + && SSLLogger.isOn(SSLLogger.Opt.HANDSHAKE)) { + SSLLogger.fine("Caching CompressedCertificate message"); + } + + cache.put(key, compressedCertMsg); + } + } + + if (compressedCertMsg == null || compressedCertMsg.length == 0) { + throw hc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "No compressed Certificate data"); + } + + CompressedCertMessage ccm = new CompressedCertMessage(hc, + hc.certDeflater.getKey(), certMsg.length, + compressedCertMsg); + + if (SSLLogger.isOn() && SSLLogger.isOn(SSLLogger.Opt.HANDSHAKE)) { + SSLLogger.fine( + "Produced CompressedCertificate handshake message", + ccm); + } + + ccm.write(hc.handshakeOutput); + hc.handshakeOutput.flush(); + + // The handshake message has been delivered. + return null; + } + } + + /** + * The "CompressedCertificate" handshake message consumer for TLS 1.3. + */ + private static final class CompressedCertConsumer implements SSLConsumer { + + // Prevent instantiation of this class. + private CompressedCertConsumer() { + // blank + } + + @Override + public void consume(ConnectionContext context, + ByteBuffer message) throws IOException { + // The consuming happens in handshake context only. + HandshakeContext hc = (HandshakeContext) context; + + // clean up this consumer + hc.handshakeConsumers.remove( + SSLHandshake.COMPRESSED_CERTIFICATE.id); + hc.handshakeConsumers.remove(SSLHandshake.CERTIFICATE.id); + + // Parse the handshake message + CompressedCertMessage ccm = new CompressedCertMessage(hc, message); + if (SSLLogger.isOn() && SSLLogger.isOn(SSLLogger.Opt.HANDSHAKE)) { + SSLLogger.fine( + "Consuming CompressedCertificate handshake message", + ccm); + } + + // check the compression algorithm + Function inflater = + hc.certInflaters.get(ccm.algorithmId); + if (inflater == null) { + throw hc.conContext.fatal(Alert.BAD_CERTIFICATE, + "Unsupported certificate compression algorithm"); + } + + // decompress + byte[] certificateMessage = inflater.apply(ccm.compressedCert); + + // check the uncompressed length + if (certificateMessage == null || + certificateMessage.length != ccm.uncompressedLength) { + throw hc.conContext.fatal(Alert.BAD_CERTIFICATE, + "Improper certificate compression"); + } + + // Call the Certificate handshake message consumer. + CertificateMessage.t13HandshakeConsumer.consume(hc, + ByteBuffer.wrap(certificateMessage)); + } + } +} diff --git a/src/java.base/share/classes/sun/security/ssl/CompressionAlgorithm.java b/src/java.base/share/classes/sun/security/ssl/CompressionAlgorithm.java new file mode 100644 index 00000000000..3e9ef154424 --- /dev/null +++ b/src/java.base/share/classes/sun/security/ssl/CompressionAlgorithm.java @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (c) 2026, 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 sun.security.ssl; + +import java.io.ByteArrayOutputStream; +import java.util.AbstractMap; +import java.util.Map; +import java.util.function.Function; +import java.util.zip.Deflater; +import java.util.zip.Inflater; + +/** + * Enum for TLS certificate compression algorithms. + * This class also defines internally supported inflate/deflate functions. + */ + +enum CompressionAlgorithm { + ZLIB(1); // Currently only ZLIB is supported. + + final int id; + + CompressionAlgorithm(int id) { + this.id = id; + } + + static CompressionAlgorithm nameOf(int id) { + for (CompressionAlgorithm ca : CompressionAlgorithm.values()) { + if (ca.id == id) { + return ca; + } + } + + return null; + } + + // Return the size of a compression algorithms structure in TLS record. + static int sizeInRecord() { + return 2; + } + + // The size of compression/decompression buffer. + private static final int BUF_SIZE = 1024; + + private static final Map> DEFLATORS = + Map.of(ZLIB.id, (input) -> { + try (Deflater deflater = new Deflater(); + ByteArrayOutputStream outputStream = + new ByteArrayOutputStream(input.length)) { + + deflater.setInput(input); + deflater.finish(); + byte[] buffer = new byte[BUF_SIZE]; + + while (!deflater.finished()) { + int compressedSize = deflater.deflate(buffer); + outputStream.write(buffer, 0, compressedSize); + } + + return outputStream.toByteArray(); + } catch (Exception e) { + if (SSLLogger.isOn() + && SSLLogger.isOn(SSLLogger.Opt.HANDSHAKE)) { + SSLLogger.warning("Exception during certificate " + + "compression: ", e); + } + return null; + } + }); + + static Map.Entry> selectDeflater( + int[] compressionAlgorithmIds) { + + for (var entry : DEFLATORS.entrySet()) { + for (int id : compressionAlgorithmIds) { + if (id == entry.getKey()) { + return new AbstractMap.SimpleImmutableEntry<>(entry); + } + } + } + + return null; + } + + private static final Map> INFLATORS = + Map.of(ZLIB.id, (input) -> { + try (Inflater inflater = new Inflater(); + ByteArrayOutputStream outputStream = + new ByteArrayOutputStream(input.length)) { + + inflater.setInput(input); + byte[] buffer = new byte[BUF_SIZE]; + + while (!inflater.finished()) { + int decompressedSize = inflater.inflate(buffer); + + if (decompressedSize == 0) { + if (inflater.needsDictionary()) { + if (SSLLogger.isOn() + && SSLLogger.isOn( + SSLLogger.Opt.HANDSHAKE)) { + SSLLogger.warning("Compressed input " + + "requires a dictionary"); + } + + return null; + } + + if (inflater.needsInput()) { + if (SSLLogger.isOn() + && SSLLogger.isOn( + SSLLogger.Opt.HANDSHAKE)) { + SSLLogger.warning( + "Incomplete compressed input"); + } + + return null; + } + + // Else just break the loop. + break; + } + + outputStream.write(buffer, 0, decompressedSize); + + // Bound the memory usage. + if (outputStream.size() + > SSLConfiguration.maxHandshakeMessageSize) { + if (SSLLogger.isOn() + && SSLLogger.isOn( + SSLLogger.Opt.HANDSHAKE)) { + SSLLogger.warning("The size of the " + + "uncompressed certificate message " + + "exceeds maximum allowed size of " + + SSLConfiguration.maxHandshakeMessageSize + + " bytes; compressed size: " + + input.length); + } + + return null; + } + } + + return outputStream.toByteArray(); + } catch (Exception e) { + if (SSLLogger.isOn() + && SSLLogger.isOn(SSLLogger.Opt.HANDSHAKE)) { + SSLLogger.warning( + "Exception during certificate decompression: ", + e); + } + return null; + } + }); + + static Map> getInflaters() { + return INFLATORS; + } +} diff --git a/src/java.base/share/classes/sun/security/ssl/DHasKEM.java b/src/java.base/share/classes/sun/security/ssl/DHasKEM.java index 763013f280c..ef5c5b82f06 100644 --- a/src/java.base/share/classes/sun/security/ssl/DHasKEM.java +++ b/src/java.base/share/classes/sun/security/ssl/DHasKEM.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, 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 @@ -101,7 +101,18 @@ public class DHasKEM implements KEMSpi { return new KEM.Encapsulated( sub(dh, from, to), pkEm, null); + + } catch (IllegalArgumentException e) { + // ECDH validation failure + // all-zero shared secret + throw e; + } catch (InvalidKeyException e) { + // Invalid peer public key + // Convert InvalidKeyException to an unchecked exception + throw new IllegalArgumentException("Invalid peer public key", + e); } catch (Exception e) { + // Unexpected internal failure throw new ProviderException("internal error", e); } } @@ -126,6 +137,11 @@ public class DHasKEM implements KEMSpi { PublicKey pkE = params.DeserializePublicKey(encapsulation); SecretKey dh = params.DH(algorithm, skR, pkE); return sub(dh, from, to); + + } catch (IllegalArgumentException e) { + // ECDH validation failure + // all-zero shared secret + throw e; } catch (IOException | InvalidKeyException e) { throw new DecapsulateException("Cannot decapsulate", e); } catch (Exception e) { @@ -248,7 +264,24 @@ public class DHasKEM implements KEMSpi { KeyAgreement ka = KeyAgreement.getInstance(kaAlgorithm); ka.init(skE); ka.doPhase(pkR, true); - return ka.generateSecret(alg); + SecretKey secret = ka.generateSecret(alg); + + // RFC 8446 section 7.4.2: checks for all-zero + // X25519/X448 shared secret. + if (kaAlgorithm.equals("X25519") || + kaAlgorithm.equals("X448")) { + byte[] s = secret.getEncoded(); + for (byte b : s) { + if (b != 0) { + return secret; + } + } + // Trigger ILLEGAL_PARAMETER alert + throw new IllegalArgumentException( + "All-zero shared secret"); + } + + return secret; } } } diff --git a/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java b/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java index 54a2650c058..fbf2c00bbb4 100644 --- a/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java +++ b/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -33,6 +33,7 @@ import java.security.AlgorithmConstraints; import java.security.CryptoPrimitive; import java.util.*; import java.util.AbstractMap.SimpleImmutableEntry; +import java.util.function.Function; import javax.crypto.SecretKey; import javax.net.ssl.SNIServerName; import javax.net.ssl.SSLHandshakeException; @@ -131,6 +132,10 @@ abstract class HandshakeContext implements ConnectionContext { List peerRequestedSignatureSchemes; List peerRequestedCertSignSchemes; + // CertificateCompressionAlgorithm + Map> certInflaters; + Map.Entry> certDeflater; + // Known authorities X500Principal[] peerSupportedAuthorities = null; diff --git a/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java b/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java index 39e82b50435..0ca197160a9 100644 --- a/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java +++ b/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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,6 +26,7 @@ package sun.security.ssl; import sun.security.util.RawKeySpec; +import javax.crypto.DecapsulateException; import javax.crypto.KDF; import javax.crypto.KEM; import javax.crypto.KeyAgreement; @@ -35,6 +36,7 @@ import javax.net.ssl.SSLHandshakeException; import java.io.IOException; import java.security.GeneralSecurityException; +import java.security.InvalidKeyException; import java.security.KeyFactory; import java.security.PrivateKey; import java.security.Provider; @@ -47,6 +49,9 @@ import sun.security.util.KeyUtil; */ public class KAKeyDerivation implements SSLKeyDerivation { + // Algorithm used to derive TLS 1.3 shared secrets + private static final String t13KeyDerivationAlgorithm = + System.getProperty("jdk.tls.t13KeyDerivationAlgorithm", "Generic"); private final String algorithmName; private final HandshakeContext context; private final PrivateKey localPrivateKey; @@ -173,6 +178,9 @@ public class KAKeyDerivation implements SSLKeyDerivation { "encapsulation"); } + // All exceptions thrown during KEM encapsulation are mapped + // to TLS fatal alerts: + // illegal_parameter alert or internal_error alert. try { KeyFactory kf = (provider != null) ? KeyFactory.getInstance(algorithmName, provider) : @@ -189,8 +197,18 @@ public class KAKeyDerivation implements SSLKeyDerivation { SecretKey derived = deriveHandshakeSecret(algorithm, sharedSecret); return new KEM.Encapsulated(derived, enc.encapsulation(), null); - } catch (GeneralSecurityException gse) { - throw new SSLHandshakeException("Could not generate secret", gse); + } catch (IllegalArgumentException | InvalidKeyException e) { + // Peer validation failure + // ECDH all-zero shared secret (RFC 8446 section 7.4.2), + // ML-KEM encapsulation key check failure (FIPS-203 section 7.2) + throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER, e); + } catch (GeneralSecurityException e) { + // Cryptographic failure, + // deriveHandshakeSecret failure. + throw context.conContext.fatal(Alert.INTERNAL_ERROR, e); + } catch (RuntimeException e) { + // unexpected provider/runtime failure + throw context.conContext.fatal(Alert.INTERNAL_ERROR, e); } finally { KeyUtil.destroySecretKeys(sharedSecret); } @@ -208,23 +226,41 @@ public class KAKeyDerivation implements SSLKeyDerivation { // Using KEM: called by the client after receiving the KEM // ciphertext (keyshare) from the server in ServerHello. // The client decapsulates it using its private key. - KEM kem = (provider != null) - ? KEM.getInstance(algorithmName, provider) - : KEM.getInstance(algorithmName); - var decapsulator = kem.newDecapsulator(localPrivateKey); - sharedSecret = decapsulator.decapsulate( - keyshare, 0, decapsulator.secretSize(), - "TlsPremasterSecret"); + + // All exceptions thrown during KEM decapsulation are mapped + // to TLS fatal alerts: + // illegal_parameter alert or internal_error alert. + try { + KEM kem = (provider != null) + ? KEM.getInstance(algorithmName, provider) + : KEM.getInstance(algorithmName); + var decapsulator = kem.newDecapsulator(localPrivateKey); + sharedSecret = decapsulator.decapsulate( + keyshare, 0, decapsulator.secretSize(), + t13KeyDerivationAlgorithm); + } catch (IllegalArgumentException | InvalidKeyException | + DecapsulateException e) { + // Peer validation failure + // ECDH all-zero shared secret (RFC 8446 section 7.4.2) + throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER, e); + } catch (GeneralSecurityException e) { + // cryptographic failure + throw context.conContext.fatal(Alert.INTERNAL_ERROR, e); + } catch (RuntimeException e) { + // unexpected provider/runtime failure + throw context.conContext.fatal(Alert.INTERNAL_ERROR, e); + } } else { // Using traditional DH-style Key Agreement KeyAgreement ka = KeyAgreement.getInstance(algorithmName); ka.init(localPrivateKey); ka.doPhase(peerPublicKey, true); - sharedSecret = ka.generateSecret("TlsPremasterSecret"); + sharedSecret = ka.generateSecret(t13KeyDerivationAlgorithm); } return deriveHandshakeSecret(type, sharedSecret); } catch (GeneralSecurityException gse) { + // deriveHandshakeSecret() failure throw new SSLHandshakeException("Could not generate secret", gse); } finally { KeyUtil.destroySecretKeys(sharedSecret); diff --git a/src/java.base/share/classes/sun/security/ssl/QuicTLSEngineImpl.java b/src/java.base/share/classes/sun/security/ssl/QuicTLSEngineImpl.java index 74975fc1e5b..3384bf5f089 100644 --- a/src/java.base/share/classes/sun/security/ssl/QuicTLSEngineImpl.java +++ b/src/java.base/share/classes/sun/security/ssl/QuicTLSEngineImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -73,6 +73,7 @@ public final class QuicTLSEngineImpl implements QuicTLSEngine, SSLTransport { SSLHandshake.ENCRYPTED_EXTENSIONS.id, HANDSHAKE, SSLHandshake.CERTIFICATE_REQUEST.id, HANDSHAKE, SSLHandshake.CERTIFICATE.id, HANDSHAKE, + SSLHandshake.COMPRESSED_CERTIFICATE.id, HANDSHAKE, SSLHandshake.CERTIFICATE_VERIFY.id, HANDSHAKE, SSLHandshake.FINISHED.id, HANDSHAKE, SSLHandshake.NEW_SESSION_TICKET.id, ONE_RTT); @@ -660,7 +661,7 @@ public final class QuicTLSEngineImpl implements QuicTLSEngine, SSLTransport { } Alert alert = ((QuicEngineOutputRecord) conContext.outputRecord).getAlert(); - throw new QuicTransportException(alert.description, keySpace, 0, + throw new QuicTransportException(e.getMessage(), keySpace, 0, BASE_CRYPTO_ERROR + alert.id, e); } catch (IOException e) { throw new RuntimeException(e); diff --git a/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java b/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java index a1cc3ee112f..fdeb94bb496 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java @@ -34,7 +34,9 @@ import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; import javax.net.ssl.*; import sun.security.provider.certpath.AlgorithmChecker; +import sun.security.ssl.CompressedCertificate.CompCertCacheKey; import sun.security.ssl.SSLAlgorithmConstraints.SIGNATURE_CONSTRAINTS_MODE; +import sun.security.util.Cache; import sun.security.validator.Validator; /** @@ -73,6 +75,10 @@ public abstract class SSLContextImpl extends SSLContextSpi { private final ReentrantLock contextLock = new ReentrantLock(); + // Avoid compressing local certificates repeatedly for every handshake. + private final Cache compCertCache = + Cache.newSoftMemoryCache(12); + SSLContextImpl() { ephemeralKeyManager = new EphemeralKeyManager(); clientCache = new SSLSessionContextImpl(false); @@ -225,6 +231,10 @@ public abstract class SSLContextImpl extends SSLContextSpi { return ephemeralKeyManager; } + Cache getCompCertCache() { + return compCertCache; + } + // Used for DTLS in server mode only. HelloCookieManager getHelloCookieManager(ProtocolVersion protocolVersion) { if (helloCookieManagerBuilder == null) { diff --git a/src/java.base/share/classes/sun/security/ssl/SSLExtension.java b/src/java.base/share/classes/sun/security/ssl/SSLExtension.java index aacb9420748..b13edc0359c 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLExtension.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -270,6 +270,27 @@ enum SSLExtension implements SSLStringizer { // Extensions defined in RFC 7924 (TLS Cached Information Extension) CACHED_INFO (0x0019, "cached_info"), + // Extensions defined in RFC 8879 (TLS Certificate Compression) + CH_COMPRESS_CERTIFICATE (0x001B, "compress_certificate", + SSLHandshake.CLIENT_HELLO, + ProtocolVersion.PROTOCOLS_OF_13, + CompressCertExtension.chNetworkProducer, + CompressCertExtension.chOnLoadConsumer, + null, + null, + null, + CompressCertExtension.ccStringizer), + + CR_COMPRESS_CERTIFICATE (0x001B, "compress_certificate", + SSLHandshake.CERTIFICATE_REQUEST, + ProtocolVersion.PROTOCOLS_OF_13, + CompressCertExtension.crNetworkProducer, + CompressCertExtension.crOnLoadConsumer, + null, + null, + null, + CompressCertExtension.ccStringizer), + // Extensions defined in RFC 5077 (TLS Session Resumption without Server-Side State) CH_SESSION_TICKET (0x0023, "session_ticket", SSLHandshake.CLIENT_HELLO, diff --git a/src/java.base/share/classes/sun/security/ssl/SSLHandshake.java b/src/java.base/share/classes/sun/security/ssl/SSLHandshake.java index 7c78f6c3005..2c6b58bafa5 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLHandshake.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLHandshake.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2026, 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 @@ -370,7 +370,22 @@ enum SSLHandshake implements SSLConsumer, HandshakeProducer { }), // RFC 8879 - TLS Certificate Compression - COMPRESSED_CERTIFICATE ((byte)0x19, "compressed_certificate"), + @SuppressWarnings({"unchecked", "rawtypes"}) + COMPRESSED_CERTIFICATE ((byte)0x19, "compressed_certificate", + (new Map.Entry[] { + new SimpleImmutableEntry<>( + CompressedCertificate.handshakeConsumer, + ProtocolVersion.PROTOCOLS_OF_13 + ) + }), + (new Map.Entry[] { + // Note that the producing of this message is delegated to + // CertificateMessage producer. + new SimpleImmutableEntry<>( + CertificateMessage.t13HandshakeProducer, + ProtocolVersion.PROTOCOLS_OF_13 + ) + })), // RFC 8870 - Encrypted Key Transport for DTLS/Secure RTP EKT_KEY ((byte)0x1A, "ekt_key"), diff --git a/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java b/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java index f603cc22949..cef2f43526a 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2026, 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 @@ -28,13 +28,13 @@ package sun.security.ssl; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; -import java.io.InterruptedIOException; import java.io.OutputStream; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketAddress; import java.net.SocketException; +import java.net.SocketTimeoutException; import java.nio.ByteBuffer; import java.util.List; import java.util.concurrent.TimeUnit; @@ -77,10 +77,10 @@ public final class SSLSocketImpl /** * ERROR HANDLING GUIDELINES * (which exceptions to throw and catch and which not to throw and catch) - * + *

* - if there is an IOException (SocketException) when accessing the * underlying Socket, pass it through - * + *

* - do not throw IOExceptions, throw SSLExceptions (or a subclass) */ @@ -454,12 +454,12 @@ public final class SSLSocketImpl if (!conContext.isNegotiated) { readHandshakeRecord(); } - } catch (InterruptedIOException iioe) { + } catch (SocketTimeoutException e) { if(resumable){ - handleException(iioe); + handleException(e); } else{ throw conContext.fatal(Alert.HANDSHAKE_FAILURE, - "Couldn't kickstart handshaking", iioe); + "Couldn't kickstart handshaking", e); } } catch (SocketException se) { handleException(se); @@ -1427,7 +1427,7 @@ public final class SSLSocketImpl return 0; } } catch (SSLException | - InterruptedIOException | SocketException se) { + SocketTimeoutException | SocketException se) { // Don't change exception in case of timeouts or interrupts // or SocketException. throw se; @@ -1486,7 +1486,7 @@ public final class SSLSocketImpl return buffer; } } catch (SSLException | - InterruptedIOException | SocketException se) { + SocketTimeoutException | SocketException se) { // Don't change exception in case of timeouts or interrupts // or SocketException. throw se; @@ -1677,40 +1677,23 @@ public final class SSLSocketImpl SSLLogger.warning("handling exception", cause); } - // Don't close the Socket in case of timeouts or interrupts. - if (cause instanceof InterruptedIOException) { - throw (IOException)cause; - } - - // need to perform error shutdown - boolean isSSLException = (cause instanceof SSLException); - Alert alert; - if (isSSLException) { - if (cause instanceof SSLHandshakeException) { - alert = Alert.HANDSHAKE_FAILURE; - } else { - alert = Alert.UNEXPECTED_MESSAGE; + throw switch (cause) { + // Don't close the Socket in case of timeouts. + case SocketTimeoutException ste -> ste; + // Send TLS alert with "fatal", then throw the socket exception. + case SocketException se -> { + try { + throw conContext.fatal(Alert.UNEXPECTED_MESSAGE, se); + } catch (Exception _) { + } + yield se; } - } else { - if (cause instanceof IOException) { - alert = Alert.UNEXPECTED_MESSAGE; - } else { - // RuntimeException - alert = Alert.INTERNAL_ERROR; - } - } - - if (cause instanceof SocketException) { - try { - throw conContext.fatal(alert, cause); - } catch (Exception e) { - // Just delivering the fatal alert, re-throw the socket exception instead. - } - - throw (SocketException)cause; - } - - throw conContext.fatal(alert, cause); + case SSLHandshakeException sslhe -> + conContext.fatal(Alert.HANDSHAKE_FAILURE, sslhe); + case IOException ioe -> + conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe); + default -> conContext.fatal(Alert.INTERNAL_ERROR, cause); + }; } private Plaintext handleEOF(EOFException eofe) throws IOException { diff --git a/src/java.base/share/classes/sun/security/ssl/SSLSocketInputRecord.java b/src/java.base/share/classes/sun/security/ssl/SSLSocketInputRecord.java index fd9c4b171e7..fc3d9733150 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLSocketInputRecord.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLSocketInputRecord.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, Azul Systems, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -27,10 +27,10 @@ package sun.security.ssl; import java.io.EOFException; -import java.io.InterruptedIOException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.net.SocketTimeoutException; import java.nio.ByteBuffer; import java.security.GeneralSecurityException; import java.util.ArrayList; @@ -180,7 +180,7 @@ final class SSLSocketInputRecord extends InputRecord implements SSLRecord { if (plaintext == null) { plaintext = decodeInputRecord(); } - } catch(InterruptedIOException e) { + } catch (SocketTimeoutException e) { // do not clean header and recordBody in case of Socket Timeout cleanInBuffer = false; throw e; diff --git a/src/java.base/share/classes/sun/security/ssl/SSLTransport.java b/src/java.base/share/classes/sun/security/ssl/SSLTransport.java index 50bff1e6d21..02551b4a8c1 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLTransport.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLTransport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -27,8 +27,8 @@ package sun.security.ssl; import java.io.EOFException; import java.io.IOException; -import java.io.InterruptedIOException; import java.net.SocketException; +import java.net.SocketTimeoutException; import java.nio.ByteBuffer; import javax.crypto.AEADBadTagException; import javax.crypto.BadPaddingException; @@ -138,7 +138,7 @@ interface SSLTransport { } catch (EOFException eofe) { // rethrow EOFException, the call will handle it if needed. throw eofe; - } catch (InterruptedIOException | SocketException se) { + } catch (SocketTimeoutException | SocketException se) { // don't close the Socket in case of timeouts or interrupts or SocketException. throw se; } catch (IOException ioe) { diff --git a/src/java.base/share/classes/sun/security/ssl/ServerHello.java b/src/java.base/share/classes/sun/security/ssl/ServerHello.java index 6980c216697..4bd2b0a059f 100644 --- a/src/java.base/share/classes/sun/security/ssl/ServerHello.java +++ b/src/java.base/share/classes/sun/security/ssl/ServerHello.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -1461,6 +1461,11 @@ final class ServerHello { chc.handshakeConsumers.put( SSLHandshake.CERTIFICATE_REQUEST.id, SSLHandshake.CERTIFICATE_REQUEST); + if (chc.certInflaters != null && !chc.certInflaters.isEmpty()) { + chc.handshakeConsumers.put( + SSLHandshake.COMPRESSED_CERTIFICATE.id, + SSLHandshake.COMPRESSED_CERTIFICATE); + } chc.handshakeConsumers.put( SSLHandshake.CERTIFICATE.id, SSLHandshake.CERTIFICATE); diff --git a/src/java.base/share/classes/sun/security/util/KeyUtil.java b/src/java.base/share/classes/sun/security/util/KeyUtil.java index 942a91d61b8..e9dabdc5b06 100644 --- a/src/java.base/share/classes/sun/security/util/KeyUtil.java +++ b/src/java.base/share/classes/sun/security/util/KeyUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, 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 @@ -444,15 +444,16 @@ public final class KeyUtil { // is the LMS public key for the top-level tree. // Section 5.3: LMS public key is u32str(type) || u32str(otstype) || I || T[1] // Section 8: type is the numeric identifier for an LMS specification. - // This RFC defines 5 SHA-256 based types, value from 5 to 9. if (rawKey.length < 8) { throw new NoSuchAlgorithmException("Cannot decode public key"); } int num = ((rawKey[4] & 0xff) << 24) + ((rawKey[5] & 0xff) << 16) + ((rawKey[6] & 0xff) << 8) + (rawKey[7] & 0xff); return switch (num) { - // RFC 8554 only supports SHA_256 hash algorithm + // RFC 8554 only supports SHA_256 hash algorithms case 5, 6, 7, 8, 9 -> AlgorithmId.SHA256_oid; + // RFC 9858 supports SHAKE_256 hash algorithms + case 15, 16, 17, 18, 19 -> AlgorithmId.SHAKE256_512_oid; default -> throw new NoSuchAlgorithmException("Unknown LMS type: " + num); }; } catch (IOException e) { diff --git a/src/java.base/share/classes/sun/security/util/Password.java b/src/java.base/share/classes/sun/security/util/Password.java index 02cdcaf53fd..c1b44856c8b 100644 --- a/src/java.base/share/classes/sun/security/util/Password.java +++ b/src/java.base/share/classes/sun/security/util/Password.java @@ -29,6 +29,7 @@ import java.io.*; import java.nio.*; import java.nio.charset.*; import java.util.Arrays; +import java.util.Locale; import jdk.internal.access.SharedSecrets; import jdk.internal.io.JdkConsoleImpl; @@ -43,6 +44,22 @@ public class Password { return readPassword(in, false); } + private static final boolean ALLOW_STDIN; + static { + var value = SecurityProperties.getOverridableProperty( + "jdk.security.password.allowSystemIn"); + if (value != null) { + value = value.toLowerCase(Locale.ROOT); + } + ALLOW_STDIN = switch (value) { + case null -> true; // Default true now + case "true" -> true; + case "false" -> false; + default -> throw new IllegalArgumentException( + "Invalid jdk.security.password.allowSystemIn value: " + value); + }; + } + /** Reads user password from given input stream. * @param isEchoOn true if the password should be echoed on the screen */ @@ -66,19 +83,23 @@ public class Password { } consoleBytes = ConsoleHolder.convertToBytes(consoleEntered); in = new ByteArrayInputStream(consoleBytes); - } else if (in == System.in && VM.isBooted() - && System.in.available() == 0) { - // Warn if reading password from System.in but it's empty. - // This may be running in an IDE Run Window or in JShell, - // which acts like an interactive console and echoes the - // entered password. In this case, print a warning that - // the password might be echoed. If available() is not zero, - // it's more likely the input comes from a pipe, such as - // "echo password |" or "cat password_file |" where input - // will be silently consumed without echoing to the screen. - // Warn only if VM is booted and ResourcesMgr is available. - System.err.print(ResourcesMgr.getString - ("warning.input.may.be.visible.on.screen")); + } else if (in == System.in) { + if (!ALLOW_STDIN) { + throw new UnsupportedOperationException("Console not available." + + " Reading passwords from standard input is disallowed."); + } else if (VM.isBooted() && in.available() == 0) { + // Warn if reading password from System.in but it's empty. + // This may be running in an IDE Run Window or in JShell, + // which acts like an interactive console and echoes the + // entered password. In this case, print a warning that + // the password might be echoed. If available() is not zero, + // it's more likely the input comes from a pipe, such as + // "echo password |" or "cat password_file |" where input + // will be silently consumed without echoing to the screen. + // Warn only if VM is booted and ResourcesMgr is available. + System.err.print(ResourcesMgr.getString + ("warning.input.may.be.visible.on.screen")); + } } } diff --git a/src/java.base/share/classes/sun/util/locale/provider/LocaleResources.java b/src/java.base/share/classes/sun/util/locale/provider/LocaleResources.java index ac43b22a3bd..76b383c03e1 100644 --- a/src/java.base/share/classes/sun/util/locale/provider/LocaleResources.java +++ b/src/java.base/share/classes/sun/util/locale/provider/LocaleResources.java @@ -105,6 +105,9 @@ public class LocaleResources { // TimeZoneNamesBundle exemplar city prefix private static final String TZNB_EXCITY_PREFIX = "timezone.excity."; + // TimeZoneNamesBundle explicit metazone dst offset prefix + private static final String TZNB_METAZONE_DSTOFFSET_PREFIX = "metazone.dstoffset."; + // null singleton cache value private static final Object NULLOBJECT = new Object(); @@ -321,7 +324,8 @@ public class LocaleResources { if (Objects.isNull(data) || Objects.isNull(val = data.get())) { TimeZoneNamesBundle tznb = localeData.getTimeZoneNames(locale); - if (key.startsWith(TZNB_EXCITY_PREFIX)) { + if (key.startsWith(TZNB_EXCITY_PREFIX) || + key.startsWith(TZNB_METAZONE_DSTOFFSET_PREFIX)) { if (tznb.containsKey(key)) { val = tznb.getString(key); assert val instanceof String; @@ -378,7 +382,8 @@ public class LocaleResources { Set value = new LinkedHashSet<>(); Set tzIds = new HashSet<>(Arrays.asList(TimeZone.getAvailableIDs())); for (String key : keyset) { - if (!key.startsWith(TZNB_EXCITY_PREFIX)) { + if (!key.startsWith(TZNB_EXCITY_PREFIX) && + !key.startsWith(TZNB_METAZONE_DSTOFFSET_PREFIX)) { value.add(rb.getStringArray(key)); tzIds.remove(key); } diff --git a/src/java.base/share/classes/sun/util/locale/provider/TimeZoneNameUtility.java b/src/java.base/share/classes/sun/util/locale/provider/TimeZoneNameUtility.java index fd3d4965db3..6c684e176c8 100644 --- a/src/java.base/share/classes/sun/util/locale/provider/TimeZoneNameUtility.java +++ b/src/java.base/share/classes/sun/util/locale/provider/TimeZoneNameUtility.java @@ -37,7 +37,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.spi.TimeZoneNameProvider; import sun.util.calendar.ZoneInfo; import sun.util.cldr.CLDRLocaleProviderAdapter; -import static sun.util.locale.provider.LocaleProviderAdapter.Type; +import static sun.util.locale.provider.LocaleProviderAdapter.Type.CLDR; /** * Utility class that deals with the localized time zone names @@ -169,10 +169,22 @@ public final class TimeZoneNameUtility { * Returns the canonical ID for the given ID */ public static Optional canonicalTZID(String id) { - return ((CLDRLocaleProviderAdapter)LocaleProviderAdapter.forType(Type.CLDR)) + return ((CLDRLocaleProviderAdapter)LocaleProviderAdapter.forType(CLDR)) .canonicalTZID(id); } + /** + * {@return the explicit metazone DST offset for the specified time zone ID, if exists} + * @param tzid the time zone ID + */ + public static String explicitDstOffset(String tzid) { + return (String) (LocaleProviderAdapter.forType(CLDR) instanceof CLDRLocaleProviderAdapter ca ? + ca.getLocaleResources(Locale.ROOT) + .getTimeZoneNames("metazone.dstoffset." + + ca.canonicalTZID(tzid).orElse(tzid)) : + null); + } + private static String[] retrieveDisplayNamesImpl(String id, Locale locale) { LocaleServiceProviderPool pool = LocaleServiceProviderPool.getPool(TimeZoneNameProvider.class); diff --git a/src/java.base/share/classes/sun/util/resources/TimeZoneNamesBundle.java b/src/java.base/share/classes/sun/util/resources/TimeZoneNamesBundle.java index a30b84c6872..c5e95c8a404 100644 --- a/src/java.base/share/classes/sun/util/resources/TimeZoneNamesBundle.java +++ b/src/java.base/share/classes/sun/util/resources/TimeZoneNamesBundle.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, 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 @@ -43,8 +43,6 @@ package sun.util.resources; import java.util.Map; import java.util.LinkedHashMap; import java.util.LinkedHashSet; -import java.util.MissingResourceException; -import java.util.Objects; import java.util.Set; /** diff --git a/src/java.base/share/conf/security/java.security b/src/java.base/share/conf/security/java.security index d5d0488a004..976604b5cbc 100644 --- a/src/java.base/share/conf/security/java.security +++ b/src/java.base/share/conf/security/java.security @@ -1725,3 +1725,32 @@ com.sun.security.allowedAIALocations= # #jdk.mlkem.pkcs8.encoding = seed #jdk.mldsa.pkcs8.encoding = seed + +# +# Policy for reading passwords from System.in +# +# When Java needs to read a password, whether it's via a tool such as keytool or +# kinit, or by an API such as PasswordCallback with echo off, it normally reads +# directly from the console. If the console is not available, Java falls back +# to reading from the standard input stream ("System.in"), which typically +# represents a redirected file or an inter-process pipe. This fallback is not +# formally specified, and is not widely adopted by tools from other vendors. +# +# This security property determines whether passwords can be read from the +# standard input stream when a console is not available. The value can be set +# to either "true" or "false". If the value is set to "false", attempting +# to read passwords from the standard input stream without a console will +# throw an exception. The default value is "true". This default may change +# in a future release. +# +# If a system property of the same name is also specified, it supersedes the +# security property value defined here. +# +# Note: This property applies only to password reading from the standard input +# stream. It does not affect other supported password sources. For example, the +# JAAS KeyStoreLoginModule allows a password to be read from the user-specified +# "keyStorePasswordURL" option. The keytool and jarsigner commands also support +# options such as "-storepass:env" and "-storepass:file" that read passwords +# from an environment variable or a file. +# +#jdk.security.password.allowSystemIn = true diff --git a/src/java.base/unix/classes/java/lang/ProcessImpl.java b/src/java.base/unix/classes/java/lang/ProcessImpl.java index 00b51fb3389..d9a4547848f 100644 --- a/src/java.base/unix/classes/java/lang/ProcessImpl.java +++ b/src/java.base/unix/classes/java/lang/ProcessImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -82,8 +82,7 @@ final class ProcessImpl extends Process { private static enum LaunchMechanism { // order IS important! FORK, - POSIX_SPAWN, - VFORK + POSIX_SPAWN } /** @@ -98,29 +97,16 @@ final class ProcessImpl extends Process { try { // Should be value of a LaunchMechanism enum - LaunchMechanism lm = LaunchMechanism.valueOf(s.toUpperCase(Locale.ROOT)); - switch (OperatingSystem.current()) { - case LINUX: { - // All options are valid for Linux, but VFORK is deprecated and results - // in a warning - if (lm == LaunchMechanism.VFORK) { - System.err.println("VFORK MODE DEPRECATED"); - System.err.println(""" - The VFORK launch mechanism has been deprecated for being dangerous. - It will be removed in a future java version. Either remove the - jdk.lang.Process.launchMechanism property (preferred) or use FORK mode - instead (-Djdk.lang.Process.launchMechanism=FORK). - """); - } - return lm; - } - case AIX: - case MACOS: - if (lm != LaunchMechanism.VFORK) { - return lm; // All but VFORK are valid - } - break; + String launchMechanism = s.toUpperCase(Locale.ROOT); + if (launchMechanism.equals("VFORK") && OperatingSystem.isLinux()) { + launchMechanism = "FORK"; + System.err.println(String.format(""" + The VFORK launch mechanism has been removed. Switching to %s instead. + Please remove the jdk.lang.Process.launchMechanism property (preferred) + or use FORK mode instead (-Djdk.lang.Process.launchMechanism=FORK).%n + """, launchMechanism)); } + return LaunchMechanism.valueOf(launchMechanism); } catch (IllegalArgumentException e) { } @@ -266,7 +252,6 @@ final class ProcessImpl extends Process { *

      *   1 - fork(2) and exec(2)
      *   2 - posix_spawn(3P)
-     *   3 - vfork(2) and exec(2)
      * 
* @param fds an array of three file descriptors. * Indexes 0, 1, and 2 correspond to standard input, diff --git a/src/java.base/unix/classes/sun/nio/fs/UnixConstants.java.template b/src/java.base/unix/classes/sun/nio/fs/UnixConstants.java.template index 6823833582f..7a9a22ac40d 100644 --- a/src/java.base/unix/classes/sun/nio/fs/UnixConstants.java.template +++ b/src/java.base/unix/classes/sun/nio/fs/UnixConstants.java.template @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2026, 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 @@ -173,7 +173,5 @@ class UnixConstants { #ifdef __linux__ // advice flags used with posix_fadvise(2) static final int PREFIX_POSIX_FADV_SEQUENTIAL = POSIX_FADV_SEQUENTIAL; - static final int PREFIX_POSIX_FADV_NOREUSE = POSIX_FADV_NOREUSE; - static final int PREFIX_POSIX_FADV_WILLNEED = POSIX_FADV_WILLNEED; #endif } diff --git a/src/java.base/unix/native/libjava/ProcessImpl_md.c b/src/java.base/unix/native/libjava/ProcessImpl_md.c index f7d91166e76..69af948d2da 100644 --- a/src/java.base/unix/native/libjava/ProcessImpl_md.c +++ b/src/java.base/unix/native/libjava/ProcessImpl_md.c @@ -60,51 +60,33 @@ * changing paths... * - then exec(2) the target binary * - * There are three ways to fork off: + * On the OS-side are three ways to fork off, but we only use two of them: * - * A) fork(2). Portable and safe (no side effects) but may fail with ENOMEM on - * all Unices when invoked from a VM with a high memory footprint. On Unices - * with strict no-overcommit policy this problem is most visible. + * A) fork(2). Portable and safe (no side effects) but could fail on very ancient + * Unices that don't employ COW on fork(2). The modern platforms we support + * (Linux, MacOS, AIX) all do. It may have a small performance penalty compared + * to modern posix_spawn(3) implementations - see below. + * fork(2) can be used by specifying -Djdk.lang.Process.launchMechanism=FORK when starting + * the (parent process) JVM. * - * This is because forking the VM will first create a child process with - * theoretically the same memory footprint as the parent - even if you plan - * to follow up with exec'ing a tiny binary. In reality techniques like - * copy-on-write etc mitigate the problem somewhat but we still run the risk - * of hitting system limits. + * B) vfork(2): Portable and fast but very unsafe. For details, see JDK-8357090. + * We supported this mode in older releases but removed support for it in JDK 27. + * Modern posix_spawn(3) implementations use techniques similar to vfork(2), but + * in a much safer way * - * For a Linux centric description of this problem, see the documentation on - * /proc/sys/vm/overcommit_memory in Linux proc(5). - * - * B) vfork(2): Portable and fast but very unsafe. It bypasses the memory - * problems related to fork(2) by starting the child in the memory image of - * the parent. Things that can go wrong include: - * - Programming errors in the child process before the exec(2) call may - * trash memory in the parent process, most commonly the stack of the - * thread invoking vfork. - * - Signals received by the child before the exec(2) call may be at best - * misdirected to the parent, at worst immediately kill child and parent. - * - * This is mitigated by very strict rules about what one is allowed to do in - * the child process between vfork(2) and exec(2), which is basically nothing. - * However, we always broke this rule by doing the pre-exec work between - * vfork(2) and exec(2). - * - * Also note that vfork(2) has been deprecated by the OpenGroup, presumably - * because of its many dangers. - * - * C) clone(2): This is a Linux specific call which gives the caller fine - * grained control about how exactly the process fork is executed. It is - * powerful, but Linux-specific. - * - * Aside from these three possibilities there is a forth option: posix_spawn(3). - * Where fork/vfork/clone all fork off the process and leave pre-exec work and - * calling exec(2) to the user, posix_spawn(3) offers the user fork+exec-like - * functionality in one package, similar to CreateProcess() on Windows. - * - * It is not a system call in itself, but usually a wrapper implemented within - * the libc in terms of one of (fork|vfork|clone)+exec - so whether or not it - * has advantages over calling the naked (fork|vfork|clone) functions depends - * on how posix_spawn(3) is implemented. + * C) posix_spawn(3): Where fork/vfork/clone all fork off the process and leave + * pre-exec work and calling exec(2) to the user, posix_spawn(3) offers the user + * fork+exec-like functionality in one package, similar to CreateProcess() on Windows. + * It is not a system call, but a wrapper implemented in user-space libc in terms + * of one of (fork|vfork|clone)+exec - so whether or not it has advantages over calling + * the naked (fork|vfork|clone) functions depends on how posix_spawn(3) is implemented. + * Modern posix_spawn(3) implementations, on Linux, use clone(2) with CLONE_VM | CLONE_VFORK, + * giving us the best ratio between performance and safety. + * Note however, that posix_spawn(3) can be buggy, depending on the libc implementation. + * E.g., on MacOS, it is still fully not POSIX-compliant. Therefore, we need to retain the + * FORK mode as a backup. + * Posix_spawn mode is used by default, but can be explicitly enabled using + * -Djdk.lang.Process.launchMechanism=POSIX_SPAWN when starting the (parent process) JVM. * * Note that when using posix_spawn(3), we exec twice: first a tiny binary called * the jspawnhelper, then in the jspawnhelper we do the pre-exec work and exec a @@ -117,58 +99,14 @@ * --- Linux-specific --- * * How does glibc implement posix_spawn? - * (see: sysdeps/posix/spawni.c for glibc < 2.24, - * sysdeps/unix/sysv/linux/spawni.c for glibc >= 2.24): * - * 1) Before glibc 2.4 (released 2006), posix_spawn(3) used just fork(2)/exec(2). - * This would be bad for the JDK since we would risk the known memory issues with - * fork(2). But since this only affects glibc variants which have long been - * phased out by modern distributions, this is irrelevant. + * Before glibc 2.4 (released 2006), posix_spawn(3) used just fork(2)/exec(2). From + * glibc 2.4 up to and including 2.23, it used either fork(2) or vfork(2). None of these + * versions still matter. * - * 2) Between glibc 2.4 and glibc 2.23, posix_spawn uses either fork(2) or - * vfork(2) depending on how exactly the user called posix_spawn(3): - * - * - * The child process is created using vfork(2) instead of fork(2) when - * either of the following is true: - * - * * the spawn-flags element of the attributes object pointed to by - * attrp contains the GNU-specific flag POSIX_SPAWN_USEVFORK; or - * - * * file_actions is NULL and the spawn-flags element of the attributes - * object pointed to by attrp does not contain - * POSIX_SPAWN_SETSIGMASK, POSIX_SPAWN_SETSIGDEF, - * POSIX_SPAWN_SETSCHEDPARAM, POSIX_SPAWN_SETSCHEDULER, - * POSIX_SPAWN_SETPGROUP, or POSIX_SPAWN_RESETIDS. - * - * - * Due to the way the JDK calls posix_spawn(3), it would therefore call vfork(2). - * So we would avoid the fork(2) memory problems. However, there still remains the - * risk associated with vfork(2). But it is smaller than were we to call vfork(2) - * directly since we use the jspawnhelper, moving all pre-exec work off to after - * the first exec, thereby reducing the vulnerable time window. - * - * 3) Since glibc >= 2.24, glibc uses clone+exec: - * - * new_pid = CLONE (__spawni_child, STACK (stack, stack_size), stack_size, - * CLONE_VM | CLONE_VFORK | SIGCHLD, &args); - * - * This is even better than (2): - * - * CLONE_VM means we run in the parent's memory image, as with (2) - * CLONE_VFORK means parent waits until we exec, as with (2) - * - * However, error possibilities are further reduced since: - * - posix_spawn(3) passes a separate stack for the child to run on, eliminating - * the danger of trashing the forking thread's stack in the parent process. - * - posix_spawn(3) takes care to temporarily block all incoming signals to the - * child process until the first exec(2) has been called, - * - * TL;DR - * Calling posix_spawn(3) for glibc - * (2) < 2.24 is not perfect but still better than using plain vfork(2), since - * the chance of an error happening is greatly reduced - * (3) >= 2.24 is the best option - portable, fast and as safe as possible. + * Since glibc >= 2.24, glibc uses clone+exec with CLONE_VM | CLONE_VFORK to emulate vfork + * performance but without the inherent dangers (we run inside the parent's memory image + * and stop the parent for as long as it takes the child process to exec). * * --- * @@ -180,7 +118,6 @@ * * * - * * Based on the above analysis, we are currently defaulting to posix_spawn() * on all Unices including Linux. */ @@ -489,28 +426,6 @@ static int copystrings(char *buf, int offset, const char * const *arg) { __attribute_noinline__ #endif -/* vfork(2) is deprecated on Darwin */ -#ifndef __APPLE__ -static pid_t -vforkChild(ChildStuff *c) { - volatile pid_t resultPid; - - /* - * We separate the call to vfork into a separate function to make - * very sure to keep stack of child from corrupting stack of parent, - * as suggested by the scary gcc warning: - * warning: variable 'foo' might be clobbered by 'longjmp' or 'vfork' - */ - resultPid = vfork(); - - if (resultPid == 0) { - childProcess(c); - } - assert(resultPid != 0); /* childProcess never returns */ - return resultPid; -} -#endif - static pid_t forkChild(ChildStuff *c) { pid_t resultPid; @@ -734,11 +649,6 @@ spawnChild(JNIEnv *env, jobject process, ChildStuff *c, const char *helperpath) static pid_t startChild(JNIEnv *env, jobject process, ChildStuff *c, const char *helperpath) { switch (c->mode) { -/* vfork(2) is deprecated on Darwin*/ - #ifndef __APPLE__ - case MODE_VFORK: - return vforkChild(c); - #endif case MODE_FORK: return forkChild(c); case MODE_POSIX_SPAWN: @@ -872,9 +782,6 @@ Java_java_lang_ProcessImpl_forkAndExec(JNIEnv *env, if (resultPid < 0) { char * failMessage = "unknown"; switch (c->mode) { - case MODE_VFORK: - failMessage = "vfork failed"; - break; case MODE_FORK: failMessage = "fork failed"; break; diff --git a/src/java.base/unix/native/libjava/TimeZone_md.c b/src/java.base/unix/native/libjava/TimeZone_md.c index 39e7b726220..cd253edde60 100644 --- a/src/java.base/unix/native/libjava/TimeZone_md.c +++ b/src/java.base/unix/native/libjava/TimeZone_md.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2026, 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 @@ -352,15 +352,33 @@ getPlatformTimeZoneID() } static char * -getJavaTimezoneFromPlatform(const char *tz_buf, size_t tz_len, const char *mapfilename) { +mapPlatformToJavaTimezone(const char *java_home_dir, const char *tz) { FILE *tzmapf; + char mapfilename[PATH_MAX + 1]; char line[256]; int linecount = 0; + char *tz_buf = NULL; + char *temp_tz = NULL; char *javatz = NULL; + size_t tz_len = 0; + /* On AIX, the TZ environment variable may end with a comma + * followed by modifier fields until early AIX6.1. + * This restriction has been removed from AIX7. */ + + tz_buf = strdup(tz); + tz_len = strlen(tz_buf); + + /* Open tzmappings file, with buffer overrun check */ + if ((strlen(java_home_dir) + 15) > PATH_MAX) { + jio_fprintf(stderr, "Path %s/lib/tzmappings exceeds maximum path length\n", java_home_dir); + goto tzerr; + } + strcpy(mapfilename, java_home_dir); + strcat(mapfilename, "/lib/tzmappings"); if ((tzmapf = fopen(mapfilename, "r")) == NULL) { jio_fprintf(stderr, "can't open %s\n", mapfilename); - return NULL; + goto tzerr; } while (fgets(line, sizeof(line), tzmapf) != NULL) { @@ -413,50 +431,10 @@ getJavaTimezoneFromPlatform(const char *tz_buf, size_t tz_len, const char *mapfi break; } } - (void) fclose(tzmapf); - return javatz; -} - -static char * -mapPlatformToJavaTimezone(const char *java_home_dir, const char *tz) { - char mapfilename[PATH_MAX + 1]; - char *tz_buf = NULL; - char *javatz = NULL; - char *temp_tz = NULL; - size_t tz_len = 0; - - /* On AIX, the TZ environment variable may end with a comma - * followed by modifier fields until early AIX6.1. - * This restriction has been removed from AIX7. */ - - tz_buf = strdup(tz); - tz_len = strlen(tz_buf); - - /* Open tzmappings file, with buffer overrun check */ - if ((strlen(java_home_dir) + 15) > PATH_MAX) { - jio_fprintf(stderr, "Path %s/lib/tzmappings exceeds maximum path length\n", java_home_dir); - goto tzerr; - } - strcpy(mapfilename, java_home_dir); - strcat(mapfilename, "/lib/tzmappings"); - - // First attempt to find the Java timezone for the full tz string - javatz = getJavaTimezoneFromPlatform(tz_buf, tz_len, mapfilename); - - // If no match was found, check for timezone with truncated value - if (javatz == NULL) { - temp_tz = strchr(tz, ','); - tz_len = (temp_tz == NULL) ? strlen(tz) : temp_tz - tz; - free((void *) tz_buf); - tz_buf = (char *)malloc(tz_len + 1); - memcpy(tz_buf, tz, tz_len); - tz_buf[tz_len] = '\0'; - javatz = getJavaTimezoneFromPlatform(tz_buf, tz_len, mapfilename); - } tzerr: - if (tz_buf != NULL) { + if (tz_buf != NULL ) { free((void *) tz_buf); } diff --git a/src/java.base/unix/native/libjava/childproc.c b/src/java.base/unix/native/libjava/childproc.c index 83ee782482f..6bc15dfb40c 100644 --- a/src/java.base/unix/native/libjava/childproc.c +++ b/src/java.base/unix/native/libjava/childproc.c @@ -271,31 +271,6 @@ initVectorFromBlock(const char**vector, const char* block, int count) vector[count] = NULL; } -/** - * Exec FILE as a traditional Bourne shell script (i.e. one without #!). - * If we could do it over again, we would probably not support such an ancient - * misfeature, but compatibility wins over sanity. The original support for - * this was imported accidentally from execvp(). - */ -static void -execve_as_traditional_shell_script(const char *file, - const char *argv[], - const char *const envp[]) -{ - /* Use the extra word of space provided for us in argv by caller. */ - const char *argv0 = argv[0]; - const char *const *end = argv; - while (*end != NULL) - ++end; - memmove(argv+2, argv+1, (end-argv) * sizeof(*end)); - argv[0] = "/bin/sh"; - argv[1] = file; - execve(argv[0], (char **) argv, (char **) envp); - /* Can't even exec /bin/sh? Big trouble, but let's soldier on... */ - memmove(argv+1, argv+2, (end-argv) * sizeof(*end)); - argv[0] = argv0; -} - /** * Like execve(2), except that in case of ENOEXEC, FILE is assumed to * be a shell script and the system default shell is invoked to run it. @@ -305,16 +280,9 @@ execve_with_shell_fallback(int mode, const char *file, const char *argv[], const char *const envp[]) { - if (mode == MODE_VFORK) { - /* shared address space; be very careful. */ - execve(file, (char **) argv, (char **) envp); - if (errno == ENOEXEC) - execve_as_traditional_shell_script(file, argv, envp); - } else { - /* unshared address space; we can mutate environ. */ - environ = (char **) envp; - execvp(file, (char **) argv); - } + /* unshared address space; we can mutate environ. */ + environ = (char **) envp; + execvp(file, (char **) argv); } /** @@ -430,7 +398,7 @@ childProcess(void *arg) #endif /* File descriptor setup for non-Posix-spawn mode */ - if (p->mode != MODE_POSIX_SPAWN) { + if (p->mode == MODE_FORK) { /* Close the parent sides of the pipes. Closing pipe fds here is redundant, since markDescriptorsCloseOnExec() @@ -482,7 +450,7 @@ childProcess(void *arg) /* We moved the fail pipe fd */ fail_pipe_fd = FAIL_FILENO; - } /* end: FORK/VFORK mode */ + } /* end: FORK mode */ assert(fail_pipe_fd == FAIL_FILENO); @@ -508,12 +476,10 @@ childProcess(void *arg) goto WhyCantJohnnyExec; } - // Reset any mask signals from parent, but not in VFORK mode - if (p->mode != MODE_VFORK) { - sigset_t unblock_signals; - sigemptyset(&unblock_signals); - sigprocmask(SIG_SETMASK, &unblock_signals, NULL); - } + // Reset any mask signals from parent + sigset_t unblock_signals; + sigemptyset(&unblock_signals); + sigprocmask(SIG_SETMASK, &unblock_signals, NULL); // Children should be started with default signal disposition for SIGPIPE if (signal(SIGPIPE, SIG_DFL) == SIG_ERR) { diff --git a/src/java.base/unix/native/libjava/childproc.h b/src/java.base/unix/native/libjava/childproc.h index 27414e60137..0b02df7f3dd 100644 --- a/src/java.base/unix/native/libjava/childproc.h +++ b/src/java.base/unix/native/libjava/childproc.h @@ -81,7 +81,6 @@ extern char **environ; */ #define MODE_FORK 1 #define MODE_POSIX_SPAWN 2 -#define MODE_VFORK 3 typedef struct _ChildStuff { diff --git a/src/java.base/windows/native/libjava/java_props_md.c b/src/java.base/windows/native/libjava/java_props_md.c index e152dbe9bef..6504891af34 100644 --- a/src/java.base/windows/native/libjava/java_props_md.c +++ b/src/java.base/windows/native/libjava/java_props_md.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, 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 @@ -74,43 +74,32 @@ getEncodingInternal(LCID lcid) case 65001: strcpy(ret, "UTF-8"); break; - case 874: /* 9:Thai */ - case 932: /* 10:Japanese */ - case 949: /* 12:Korean Extended Wansung */ - case 950: /* 13:Chinese (Taiwan, Hongkong, Macau) */ - case 1361: /* 15:Korean Johab */ + case 874: /* Thai */ + case 932: /* Japanese */ + case 936: /* Chinese (Simplified) */ + case 949: /* Korean Extended Wansung */ + case 950: /* Chinese (Taiwan, Hongkong, Macau) */ + case 1361: /* Korean Johab */ ret[0] = 'M'; ret[1] = 'S'; - break; - case 936: - strcpy(ret, "GBK"); - break; - case 54936: - strcpy(ret, "GB18030"); - break; - default: - ret[0] = 'C'; - ret[1] = 'p'; - break; - } - //Traditional Chinese Windows should use MS950_HKSCS_XP as the - //default encoding, if HKSCS patch has been installed. - // "old" MS950 0xfa41 -> u+e001 - // "new" MS950 0xfa41 -> u+92db - if (strcmp(ret, "MS950") == 0) { - TCHAR mbChar[2] = {(char)0xfa, (char)0x41}; - WCHAR unicodeChar; - MultiByteToWideChar(CP_ACP, 0, mbChar, 2, &unicodeChar, 1); - if (unicodeChar == 0x92db) { - strcpy(ret, "MS950_HKSCS_XP"); - } - } else { - //SimpChinese Windows should use GB18030 as the default - //encoding, if gb18030 patch has been installed (on windows - //2000/XP, (1)Codepage 54936 will be available - //(2)simsun18030.ttc will exist under system fonts dir ) - if (strcmp(ret, "GBK") == 0 && IsValidCodePage(54936)) { + // Special handling for Chinese + if (codepage == 950) { + //Traditional Chinese Windows should use MS950_HKSCS_XP as the + //default encoding, if HKSCS patch has been installed. + // "old" MS950 0xfa41 -> u+e001 + // "new" MS950 0xfa41 -> u+92db + TCHAR mbChar[2] = {(char)0xfa, (char)0x41}; + WCHAR unicodeChar; + MultiByteToWideChar(CP_ACP, 0, mbChar, 2, &unicodeChar, 1); + if (unicodeChar == 0x92db) { + strcpy(ret, "MS950_HKSCS_XP"); + } + } else if (codepage == 936 && IsValidCodePage(54936)) { + //SimpChinese Windows should use GB18030 as the default + //encoding, if gb18030 patch has been installed (on windows + //2000/XP, (1)Codepage 54936 will be available + //(2)simsun18030.ttc will exist under system fonts dir ) char systemPath[MAX_PATH + 1]; char* gb18030Font = "\\FONTS\\SimSun18030.ttc"; FILE *f = NULL; @@ -123,6 +112,14 @@ getEncodingInternal(LCID lcid) } } } + break; + case 54936: + strcpy(ret, "GB18030"); + break; + default: + ret[0] = 'C'; + ret[1] = 'p'; + break; } return ret; diff --git a/src/java.desktop/aix/native/libawt/porting_aix.c b/src/java.desktop/aix/native/libawt/porting_aix.c deleted file mode 100644 index d8688c212d7..00000000000 --- a/src/java.desktop/aix/native/libawt/porting_aix.c +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2012, 2026 SAP SE. 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 -#include -#include -#include - -#include "porting_aix.h" - -static unsigned char dladdr_buffer[0x8000]; - -static void fill_dll_info(void) { - int rc = loadquery(L_GETINFO,dladdr_buffer, sizeof(dladdr_buffer)); - if (rc == -1) { - fprintf(stderr, "loadquery failed (%d %s)", errno, strerror(errno)); - fflush(stderr); - } -} - -static int dladdr_dont_reload(void* addr, Dl_info* info) { - const struct ld_info* p = (struct ld_info*) dladdr_buffer; - memset((void *)info, 0, sizeof(Dl_info)); - for (;;) { - if (addr >= p->ldinfo_textorg && - (char*)addr < (char*)(p->ldinfo_textorg) + p->ldinfo_textsize) { - info->dli_fname = p->ldinfo_filename; - return 1; - } - if (!p->ldinfo_next) { - break; - } - p = (struct ld_info*)(((char*)p) + p->ldinfo_next); - } - return 0; -} - -#ifdef __cplusplus -extern "C" -#endif -int dladdr(void *addr, Dl_info *info) { - static int loaded = 0; - if (!loaded) { - fill_dll_info(); - loaded = 1; - } - if (!addr) { - return 0; - } - /* Address could be AIX function descriptor? */ - void* const addr0 = *( (void**) addr ); - int rc = dladdr_dont_reload(addr, info); - if (rc == 0) { - rc = dladdr_dont_reload(addr0, info); - if (rc == 0) { - fill_dll_info(); /* refill, maybe loadquery info is outdated */ - rc = dladdr_dont_reload(addr, info); - if (rc == 0) { - rc = dladdr_dont_reload(addr0, info); - } - } - } - return rc; -} diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessible.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessible.java index 5be7f70b981..4315abe6197 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessible.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessible.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2026, 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 @@ -188,10 +188,6 @@ final class CAccessible extends CFRetainedResource implements Accessible { // Do send check box state changes to native side if (thisRole == AccessibleRole.CHECK_BOX) { - if (!Objects.equals(newValue, oldValue)) { - valueChanged(ptr); - } - // Notify native side to handle check box style menuitem if (parentRole == AccessibleRole.POPUP_MENU && newValue != null && ((AccessibleState)newValue) == AccessibleState.FOCUSED) { @@ -201,23 +197,12 @@ final class CAccessible extends CFRetainedResource implements Accessible { // Do send radio button state changes to native side if (thisRole == AccessibleRole.RADIO_BUTTON) { - if (newValue != null && !newValue.equals(oldValue)) { - valueChanged(ptr); - } - // Notify native side to handle radio button style menuitem if (parentRole == AccessibleRole.POPUP_MENU && newValue != null && ((AccessibleState)newValue) == AccessibleState.FOCUSED) { menuItemSelected(ptr); } } - - // Do send toggle button state changes to native side - if (thisRole == AccessibleRole.TOGGLE_BUTTON) { - if (!Objects.equals(newValue, oldValue)) { - valueChanged(ptr); - } - } } else if (name.equals(ACCESSIBLE_NAME_PROPERTY)) { //for now trigger only for JTabbedPane. if (e.getSource() instanceof JTabbedPane) { @@ -227,7 +212,10 @@ final class CAccessible extends CFRetainedResource implements Accessible { AccessibleRole thisRole = accessible.getAccessibleContext() .getAccessibleRole(); if (thisRole == AccessibleRole.SLIDER || - thisRole == AccessibleRole.PROGRESS_BAR) { + thisRole == AccessibleRole.PROGRESS_BAR || + thisRole == AccessibleRole.CHECK_BOX || + thisRole == AccessibleRole.RADIO_BUTTON || + thisRole == AccessibleRole.TOGGLE_BUTTON ) { valueChanged(ptr); } } diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CTextPipe.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CTextPipe.java index cf4a6e72136..ba5bb769ad5 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CTextPipe.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CTextPipe.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2026, 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 @@ -25,7 +25,6 @@ package sun.lwawt.macosx; - import java.awt.*; import java.awt.font.*; @@ -73,12 +72,17 @@ public class CTextPipe implements TextPipe { @Override public void drawString(final SunGraphics2D sg2d, final String s, final double x, final double y) { + + FontInfo info = sg2d.getFontInfo(); + double dx = x + info.originX; + double dy = y + info.originY; + final long nativeStrikePtr = getNativeStrikePtr(sg2d); if (OSXSurfaceData.IsSimpleColor(sg2d.paint) && nativeStrikePtr != 0) { final OSXSurfaceData surfaceData = (OSXSurfaceData)sg2d.getSurfaceData(); - surfaceData.drawString(this, sg2d, nativeStrikePtr, s, x, y); + surfaceData.drawString(this, sg2d, nativeStrikePtr, s, dx, dy); } else { - drawTextAsShape(sg2d, s, x, y); + drawTextAsShape(sg2d, s, dx, dy); } } @@ -153,6 +157,15 @@ public class CTextPipe implements TextPipe { final Font prevFont = sg2d.getFont(); sg2d.setFont(gV.getFont()); + int flags = gV.getLayoutFlags(); + boolean positionAdjustments = (flags & GlyphVector.FLAG_HAS_POSITION_ADJUSTMENTS) != 0; + if (positionAdjustments) { + // make sure GV positions are initialized, so they are available later in native code; this + // will already be the case if the user explicitly set the glyph positions, but not if the + // position adjustment flag was set because of a font translation transform or font tracking + gV.getGlyphPosition(0); + } + if (hasSlotData(gV)) { final int length = gV.getNumGlyphs(); float[] positions = gV.getGlyphPositions(0, length, null); @@ -177,12 +190,17 @@ public class CTextPipe implements TextPipe { @Override public void drawChars(final SunGraphics2D sg2d, final char[] data, final int offset, final int length, final int x, final int y) { + + FontInfo info = sg2d.getFontInfo(); + double dx = x + info.originX; + double dy = y + info.originY; + final long nativeStrikePtr = getNativeStrikePtr(sg2d); if (OSXSurfaceData.IsSimpleColor(sg2d.paint) && nativeStrikePtr != 0) { final OSXSurfaceData surfaceData = (OSXSurfaceData)sg2d.getSurfaceData(); - surfaceData.drawUnicodes(this, sg2d, nativeStrikePtr, data, offset, length, x, y); + surfaceData.drawUnicodes(this, sg2d, nativeStrikePtr, data, offset, length, (float) dx, (float) dy); } else { - drawTextAsShape(sg2d, new String(data, offset, length), x, y); + drawTextAsShape(sg2d, new String(data, offset, length), dx, dy); } } @@ -191,7 +209,8 @@ public class CTextPipe implements TextPipe { } public static final class Tracer extends CTextPipe { - void doDrawString(final SurfaceData sData, final long nativeStrikePtr, final String s, final float x, final float y) { + @Override + public void doDrawString(final SurfaceData sData, final long nativeStrikePtr, final String s, final double x, final double y) { GraphicsPrimitive.tracePrimitive("QuartzDrawString"); super.doDrawString(sData, nativeStrikePtr, s, x, y); } diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CheckboxAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CheckboxAccessibility.m index f2dbf60d92d..a5faf255440 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CheckboxAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CheckboxAccessibility.m @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, 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 @@ -36,6 +36,18 @@ return NSAccessibilityCheckBoxRole; } +- (NSAccessibilitySubrole _Nullable)accessibilitySubrole +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + if (env != NULL) { + NSString *javaRole = [self javaRole]; + if ([javaRole isEqualToString:@"togglebutton"]) { + return NSAccessibilityToggleSubrole; + } + } + return [super accessibilitySubrole]; +} + - (id _Nonnull) accessibilityValue { AWT_ASSERT_APPKIT_THREAD; diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m index 0f0a395c597..45e8f981f50 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m @@ -136,6 +136,7 @@ static jobject sAccessibilityClass = NULL; [rolesMap setObject:@"StaticTextAccessibility" forKey:@"label"]; [rolesMap setObject:@"RadiobuttonAccessibility" forKey:@"radiobutton"]; [rolesMap setObject:@"CheckboxAccessibility" forKey:@"checkbox"]; + [rolesMap setObject:@"CheckboxAccessibility" forKey:@"togglebutton"]; [rolesMap setObject:@"SliderAccessibility" forKey:@"slider"]; [rolesMap setObject:@"ScrollAreaAccessibility" forKey:@"scrollpane"]; [rolesMap setObject:@"ScrollBarAccessibility" forKey:@"scrollbar"]; diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifLookAndFeel.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifLookAndFeel.java index d254443b8d1..5263d248f45 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifLookAndFeel.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifLookAndFeel.java @@ -537,6 +537,10 @@ public class MotifLookAndFeel extends BasicLookAndFeel @SuppressWarnings("deprecation") final int metaMask = KeyEvent.META_MASK; + Object commonInputMap = new UIDefaults.LazyInputMap(new Object[] { + "SPACE", "pressed", + "released SPACE", "released" + }); Object[] defaults = { "Desktop.background", table.get("desktop"), @@ -593,20 +597,13 @@ public class MotifLookAndFeel extends BasicLookAndFeel "Button.foreground", table.get("controlText"), "Button.select", table.get("controlLightShadow"), "Button.font", dialogPlain12, - "Button.focusInputMap", new UIDefaults.LazyInputMap(new Object[] { - "SPACE", "pressed", - "released SPACE", "released" - }), + "Button.focusInputMap", commonInputMap, "CheckBox.textIconGap", 8, "CheckBox.margin", new InsetsUIResource(4, 2, 4, 2), "CheckBox.icon", checkBoxIcon, "CheckBox.focus", table.get("activeCaptionBorder"), - "CheckBox.focusInputMap", - new UIDefaults.LazyInputMap(new Object[] { - "SPACE", "pressed", - "released SPACE", "released" - }), + "CheckBox.focusInputMap", commonInputMap, "RadioButton.margin", new InsetsUIResource(4, 2, 4, 2), "RadioButton.textIconGap", 8, @@ -615,22 +612,14 @@ public class MotifLookAndFeel extends BasicLookAndFeel "RadioButton.icon", radioButtonIcon, "RadioButton.focus", table.get("activeCaptionBorder"), "RadioButton.icon", radioButtonIcon, - "RadioButton.focusInputMap", - new UIDefaults.LazyInputMap(new Object[] { - "SPACE", "pressed", - "released SPACE", "released" - }), + "RadioButton.focusInputMap", commonInputMap, "ToggleButton.border", toggleButtonBorder, "ToggleButton.background", table.get("control"), "ToggleButton.foreground", table.get("controlText"), "ToggleButton.focus", table.get("controlText"), "ToggleButton.select", table.get("controlLightShadow"), - "ToggleButton.focusInputMap", - new UIDefaults.LazyInputMap(new Object[] { - "SPACE", "pressed", - "released SPACE", "released" - }), + "ToggleButton.focusInputMap", commonInputMap, // Menus "Menu.border", menuMarginBorder, diff --git a/src/java.desktop/share/classes/java/awt/Dialog.java b/src/java.desktop/share/classes/java/awt/Dialog.java index 83aa89b9bf7..038aa5b65e3 100644 --- a/src/java.desktop/share/classes/java/awt/Dialog.java +++ b/src/java.desktop/share/classes/java/awt/Dialog.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2026, 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 @@ -41,7 +41,6 @@ import javax.accessibility.AccessibleRole; import javax.accessibility.AccessibleState; import javax.accessibility.AccessibleStateSet; -import sun.awt.AppContext; import sun.awt.SunToolkit; import sun.awt.util.IdentityArrayList; @@ -1013,30 +1012,12 @@ public class Dialog extends Window { if (!isModal()) { conditionalShow(null, null); } else { - AppContext showAppContext = AppContext.getAppContext(); - AtomicLong time = new AtomicLong(); Component predictedFocusOwner = null; try { predictedFocusOwner = getMostRecentFocusOwner(); if (conditionalShow(predictedFocusOwner, time)) { modalFilter = ModalEventFilter.createFilterForDialog(this); - // if this dialog is toolkit-modal, the filter should be added - // to all EDTs (for all AppContexts) - if (modalityType == ModalityType.TOOLKIT_MODAL) { - for (AppContext appContext : AppContext.getAppContexts()) { - if (appContext == showAppContext) { - continue; - } - EventQueue eventQueue = (EventQueue)appContext.get(AppContext.EVENT_QUEUE_KEY); - // it may occur that EDT for appContext hasn't been started yet, so - // we post an empty invocation event to trigger EDT initialization - eventQueue.postEvent(new InvocationEvent(this, () -> {})); - EventDispatchThread edt = eventQueue.getDispatchThread(); - edt.addEventFilter(modalFilter); - } - } - modalityPushed(); try { EventQueue eventQueue = Toolkit.getDefaultToolkit().getSystemEventQueue(); @@ -1047,19 +1028,6 @@ public class Dialog extends Window { } finally { modalityPopped(); } - - // if this dialog is toolkit-modal, its filter must be removed - // from all EDTs (for all AppContexts) - if (modalityType == ModalityType.TOOLKIT_MODAL) { - for (AppContext appContext : AppContext.getAppContexts()) { - if (appContext == showAppContext) { - continue; - } - EventQueue eventQueue = (EventQueue)appContext.get(AppContext.EVENT_QUEUE_KEY); - EventDispatchThread edt = eventQueue.getDispatchThread(); - edt.removeEventFilter(modalFilter); - } - } } } finally { if (predictedFocusOwner != null) { @@ -1482,8 +1450,7 @@ public class Dialog extends Window { return getDocumentRoot() == w.getDocumentRoot(); } case APPLICATION_MODAL: - return !w.isModalExcluded(ModalExclusionType.APPLICATION_EXCLUDE) && - (appContext == w.appContext); + return !w.isModalExcluded(ModalExclusionType.APPLICATION_EXCLUDE); case TOOLKIT_MODAL: return !w.isModalExcluded(ModalExclusionType.TOOLKIT_EXCLUDE); } diff --git a/src/java.desktop/share/classes/java/awt/EventDispatchThread.java b/src/java.desktop/share/classes/java/awt/EventDispatchThread.java index b817ca12ece..1a991741fab 100644 --- a/src/java.desktop/share/classes/java/awt/EventDispatchThread.java +++ b/src/java.desktop/share/classes/java/awt/EventDispatchThread.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2026, 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 @@ -203,8 +203,8 @@ class EventDispatchThread extends Thread { eq.dispatchEvent(event); } catch (InterruptedException interruptedException) { - doDispatch = false; // AppContext.dispose() interrupts all - // Threads in the AppContext + // keep this catch case for compatibility + doDispatch = false; } catch (Throwable e) { processException(e); diff --git a/src/java.desktop/share/classes/java/awt/KeyboardFocusManager.java b/src/java.desktop/share/classes/java/awt/KeyboardFocusManager.java index 06932d33f8a..9b55e754a64 100644 --- a/src/java.desktop/share/classes/java/awt/KeyboardFocusManager.java +++ b/src/java.desktop/share/classes/java/awt/KeyboardFocusManager.java @@ -2264,15 +2264,14 @@ public abstract class KeyboardFocusManager temporary, descendant, cause); // Fix 5028014. Rolled out. // SunToolkit.postPriorityEvent(currentFocusOwnerEvent); - SunToolkit.postEvent(currentFocusOwner.appContext, - currentFocusOwnerEvent); + SunToolkit.postEvent(currentFocusOwnerEvent); } FocusEvent newFocusOwnerEvent = new FocusEvent(descendant, FocusEvent.FOCUS_GAINED, temporary, currentFocusOwner, cause); // Fix 5028014. Rolled out. // SunToolkit.postPriorityEvent(newFocusOwnerEvent); - SunToolkit.postEvent(descendant.appContext, newFocusOwnerEvent); + SunToolkit.postEvent(newFocusOwnerEvent); if (focusLog.isLoggable(PlatformLogger.Level.FINEST)) focusLog.finest("2. SNFH_HANDLED for {0}", String.valueOf(descendant)); diff --git a/src/java.desktop/share/classes/java/awt/MouseInfo.java b/src/java.desktop/share/classes/java/awt/MouseInfo.java index 6b913adf06e..7a30243b06c 100644 --- a/src/java.desktop/share/classes/java/awt/MouseInfo.java +++ b/src/java.desktop/share/classes/java/awt/MouseInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -58,6 +58,7 @@ public class MouseInfo { * * @throws HeadlessException if GraphicsEnvironment.isHeadless() returns true * @return location of the mouse pointer + * @see GraphicsConfiguration * @since 1.5 */ public static PointerInfo getPointerInfo() throws HeadlessException { diff --git a/src/java.desktop/share/classes/java/awt/SentEvent.java b/src/java.desktop/share/classes/java/awt/SentEvent.java index 632b4ee85a8..eb85fa1453d 100644 --- a/src/java.desktop/share/classes/java/awt/SentEvent.java +++ b/src/java.desktop/share/classes/java/awt/SentEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -27,7 +27,6 @@ package java.awt; import java.io.Serial; -import sun.awt.AppContext; import sun.awt.SunToolkit; /** @@ -51,22 +50,16 @@ class SentEvent extends AWTEvent implements ActiveEvent { boolean dispatched; private AWTEvent nested; - @SuppressWarnings("serial") // Not statically typed as Serializable - private AppContext toNotify; SentEvent() { this(null); } SentEvent(AWTEvent nested) { - this(nested, null); - } - SentEvent(AWTEvent nested, AppContext toNotify) { super((nested != null) ? nested.getSource() : Toolkit.getDefaultToolkit(), ID); this.nested = nested; - this.toNotify = toNotify; } public void dispatch() { @@ -76,9 +69,6 @@ class SentEvent extends AWTEvent implements ActiveEvent { } } finally { dispatched = true; - if (toNotify != null) { - SunToolkit.postEvent(toNotify, new SentEvent()); - } synchronized (this) { notifyAll(); } @@ -86,9 +76,6 @@ class SentEvent extends AWTEvent implements ActiveEvent { } final void dispose() { dispatched = true; - if (toNotify != null) { - SunToolkit.postEvent(toNotify, new SentEvent()); - } synchronized (this) { notifyAll(); } diff --git a/src/java.desktop/share/classes/java/awt/WaitDispatchSupport.java b/src/java.desktop/share/classes/java/awt/WaitDispatchSupport.java index 71e8b3086a1..3e567f538b1 100644 --- a/src/java.desktop/share/classes/java/awt/WaitDispatchSupport.java +++ b/src/java.desktop/share/classes/java/awt/WaitDispatchSupport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2026, 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 @@ -211,8 +211,6 @@ class WaitDispatchSupport implements SecondaryLoop { } }, interval); } - // Dispose SequencedEvent we are dispatching on the current - // AppContext, to prevent us from hang - see 4531693 for details SequencedEvent currentSE = KeyboardFocusManager. getCurrentKeyboardFocusManager().getCurrentSequencedEvent(); if (currentSE != null) { diff --git a/src/java.desktop/share/classes/java/awt/Window.java b/src/java.desktop/share/classes/java/awt/Window.java index b41409a138e..23aefd8860d 100644 --- a/src/java.desktop/share/classes/java/awt/Window.java +++ b/src/java.desktop/share/classes/java/awt/Window.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2026, 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 @@ -64,7 +64,6 @@ import javax.accessibility.AccessibleState; import javax.accessibility.AccessibleStateSet; import sun.awt.AWTAccessor; -import sun.awt.AppContext; import sun.awt.DebugSettings; import sun.awt.SunToolkit; import sun.awt.util.IdentityArrayList; @@ -259,7 +258,7 @@ public class Window extends Container implements Accessible { /** * Contains all the windows that have a peer object associated, * i. e. between addNotify() and removeNotify() calls. The list - * of all Window instances can be obtained from AppContext object. + * of all Window instances can be obtained from {@link #getWindows()} * * @since 1.6 */ @@ -275,7 +274,7 @@ public class Window extends Container implements Accessible { new Vector>(); /* - * We insert a weak reference into the Vector of all Windows in AppContext + * We insert a weak reference into the Vector of all Windows * instead of 'this' so that garbage collection can still take place * correctly. */ @@ -427,11 +426,9 @@ public class Window extends Container implements Accessible { static class WindowDisposerRecord implements sun.java2d.DisposerRecord { WeakReference owner; final WeakReference weakThis; - final WeakReference context; - WindowDisposerRecord(AppContext context, Window victim) { + WindowDisposerRecord(Window victim) { weakThis = victim.weakThis; - this.context = new WeakReference(context); } public void updateOwner() { @@ -448,10 +445,7 @@ public class Window extends Container implements Accessible { parent.removeOwnedWindow(weakThis); } } - AppContext ac = context.get(); - if (null != ac) { - Window.removeFromWindowList(ac, weakThis); - } + Window.removeFromWindowList(weakThis); } } @@ -499,7 +493,7 @@ public class Window extends Container implements Accessible { } modalExclusionType = Dialog.ModalExclusionType.NO_EXCLUDE; - disposerRecord = new WindowDisposerRecord(appContext, this); + disposerRecord = new WindowDisposerRecord(this); sun.java2d.Disposer.addRecord(anchor, disposerRecord); SunToolkit.checkAndSetPolicy(this); @@ -1489,34 +1483,6 @@ public class Window extends Container implements Accessible { } } - private static Window[] getWindows(AppContext appContext) { - synchronized (Window.class) { - Window[] realCopy; - @SuppressWarnings("unchecked") - Vector> windowList = - (Vector>)appContext.get(Window.class); - if (windowList != null) { - int fullSize = windowList.size(); - int realSize = 0; - Window[] fullCopy = new Window[fullSize]; - for (int i = 0; i < fullSize; i++) { - Window w = windowList.get(i).get(); - if (w != null) { - fullCopy[realSize++] = w; - } - } - if (fullSize != realSize) { - realCopy = Arrays.copyOf(fullCopy, realSize); - } else { - realCopy = fullCopy; - } - } else { - realCopy = new Window[0]; - } - return realCopy; - } - } - /** * Returns an array of all {@code Window}s, both owned and ownerless, * created by this application. @@ -1534,7 +1500,24 @@ public class Window extends Container implements Accessible { * @since 1.6 */ public static Window[] getWindows() { - return getWindows(AppContext.getAppContext()); + synchronized (Window.class) { + Window[] realCopy; + int fullSize = windowList.size(); + int realSize = 0; + Window[] fullCopy = new Window[fullSize]; + for (int i = 0; i < fullSize; i++) { + Window w = windowList.get(i).get(); + if (w != null) { + fullCopy[realSize++] = w; + } + } + if (fullSize != realSize) { + realCopy = Arrays.copyOf(fullCopy, realSize); + } else { + realCopy = fullCopy; + } + return realCopy; + } } /** @@ -2746,30 +2729,22 @@ public class Window extends Container implements Accessible { child.disposerRecord.updateOwner(); } + private static final Vector> windowList = new Vector<>(); + private void addToWindowList() { synchronized (Window.class) { - @SuppressWarnings("unchecked") - Vector> windowList = (Vector>)appContext.get(Window.class); - if (windowList == null) { - windowList = new Vector>(); - appContext.put(Window.class, windowList); - } windowList.add(weakThis); } } - private static void removeFromWindowList(AppContext context, WeakReference weakThis) { + private static void removeFromWindowList(WeakReference weakThis) { synchronized (Window.class) { - @SuppressWarnings("unchecked") - Vector> windowList = (Vector>)context.get(Window.class); - if (windowList != null) { - windowList.remove(weakThis); - } + windowList.remove(weakThis); } } private void removeFromWindowList() { - removeFromWindowList(appContext, weakThis); + removeFromWindowList(weakThis); } /** @@ -2909,7 +2884,7 @@ public class Window extends Container implements Accessible { weakThis = new WeakReference<>(this); anchor = new Object(); - disposerRecord = new WindowDisposerRecord(appContext, this); + disposerRecord = new WindowDisposerRecord(this); sun.java2d.Disposer.addRecord(anchor, disposerRecord); addToWindowList(); diff --git a/src/java.desktop/share/classes/java/awt/geom/AffineTransform.java b/src/java.desktop/share/classes/java/awt/geom/AffineTransform.java index 9abc55d8e6f..a6869369714 100644 --- a/src/java.desktop/share/classes/java/awt/geom/AffineTransform.java +++ b/src/java.desktop/share/classes/java/awt/geom/AffineTransform.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2026, 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 @@ -1137,7 +1137,7 @@ public class AffineTransform implements Cloneable, java.io.Serializable { * The values are stored in the array as * { m00 m10 m01 m11 m02 m12 }. * An array of 4 doubles can also be specified, in which case only the - * first four elements representing the non-transform + * first four elements representing the non-translation * parts of the array are retrieved and the values are stored into * the array as { m00 m10 m01 m11 } * @param flatmatrix the double array used to store the returned diff --git a/src/java.desktop/share/classes/javax/accessibility/AccessibleContext.java b/src/java.desktop/share/classes/javax/accessibility/AccessibleContext.java index 096ca3aef44..e7fc58b0825 100644 --- a/src/java.desktop/share/classes/javax/accessibility/AccessibleContext.java +++ b/src/java.desktop/share/classes/javax/accessibility/AccessibleContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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,7 +34,6 @@ import java.beans.PropertyChangeSupport; import java.util.Locale; import sun.awt.AWTAccessor; -import sun.awt.AppContext; /** * {@code AccessibleContext} represents the minimum information all accessible @@ -84,24 +83,8 @@ public abstract class AccessibleContext { */ protected AccessibleContext() {} - /** - * The {@code AppContext} that should be used to dispatch events for this - * {@code AccessibleContext}. - */ - private volatile AppContext targetAppContext; - static { AWTAccessor.setAccessibleContextAccessor(new AWTAccessor.AccessibleContextAccessor() { - @Override - public void setAppContext(AccessibleContext accessibleContext, AppContext appContext) { - accessibleContext.targetAppContext = appContext; - } - - @Override - public AppContext getAppContext(AccessibleContext accessibleContext) { - return accessibleContext.targetAppContext; - } - @Override public Object getNativeAXResource(AccessibleContext accessibleContext) { return accessibleContext.nativeAXResource; diff --git a/src/java.desktop/share/classes/javax/print/attribute/AttributeSet.java b/src/java.desktop/share/classes/javax/print/attribute/AttributeSet.java index 58b23569a92..0fd2da05d60 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/AttributeSet.java +++ b/src/java.desktop/share/classes/javax/print/attribute/AttributeSet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -249,6 +249,7 @@ public interface AttributeSet { * @return {@code true} if the specified object is equal to this attribute * set */ + @Override public boolean equals(Object object); /** @@ -261,5 +262,6 @@ public interface AttributeSet { * * @return the hash code value for this attribute set */ + @Override public int hashCode(); } diff --git a/src/java.desktop/share/classes/javax/print/attribute/AttributeSetUtilities.java b/src/java.desktop/share/classes/javax/print/attribute/AttributeSetUtilities.java index f762eb89925..ea4dcf54f32 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/AttributeSetUtilities.java +++ b/src/java.desktop/share/classes/javax/print/attribute/AttributeSetUtilities.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -101,54 +101,67 @@ public final class AttributeSetUtilities { attrset = attributeSet; } + @Override public Attribute get(Class key) { return attrset.get(key); } + @Override public boolean add(Attribute attribute) { throw new UnmodifiableSetException(); } + @Override public synchronized boolean remove(Class category) { throw new UnmodifiableSetException(); } + @Override public boolean remove(Attribute attribute) { throw new UnmodifiableSetException(); } + @Override public boolean containsKey(Class category) { return attrset.containsKey(category); } + @Override public boolean containsValue(Attribute attribute) { return attrset.containsValue(attribute); } + @Override public boolean addAll(AttributeSet attributes) { throw new UnmodifiableSetException(); } + @Override public int size() { return attrset.size(); } + @Override public Attribute[] toArray() { return attrset.toArray(); } + @Override public void clear() { throw new UnmodifiableSetException(); } + @Override public boolean isEmpty() { return attrset.isEmpty(); } + @Override public boolean equals(Object o) { return attrset.equals (o); } + @Override public int hashCode() { return attrset.hashCode(); } @@ -366,54 +379,67 @@ public final class AttributeSetUtilities { attrset = attributeSet; } + @Override public synchronized Attribute get(Class category) { return attrset.get(category); } + @Override public synchronized boolean add(Attribute attribute) { return attrset.add(attribute); } + @Override public synchronized boolean remove(Class category) { return attrset.remove(category); } + @Override public synchronized boolean remove(Attribute attribute) { return attrset.remove(attribute); } + @Override public synchronized boolean containsKey(Class category) { return attrset.containsKey(category); } + @Override public synchronized boolean containsValue(Attribute attribute) { return attrset.containsValue(attribute); } + @Override public synchronized boolean addAll(AttributeSet attributes) { return attrset.addAll(attributes); } + @Override public synchronized int size() { return attrset.size(); } + @Override public synchronized Attribute[] toArray() { return attrset.toArray(); } + @Override public synchronized void clear() { attrset.clear(); } + @Override public synchronized boolean isEmpty() { return attrset.isEmpty(); } + @Override public synchronized boolean equals(Object o) { return attrset.equals (o); } + @Override public synchronized int hashCode() { return attrset.hashCode(); } diff --git a/src/java.desktop/share/classes/javax/print/attribute/DateTimeSyntax.java b/src/java.desktop/share/classes/javax/print/attribute/DateTimeSyntax.java index 2f0eafb9f79..154b492de84 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/DateTimeSyntax.java +++ b/src/java.desktop/share/classes/javax/print/attribute/DateTimeSyntax.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -114,6 +114,7 @@ public abstract class DateTimeSyntax implements Serializable, Cloneable { * @return {@code true} if {@code object} is equivalent to this date-time * attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return object instanceof DateTimeSyntax other && value.equals(other.value); @@ -123,6 +124,7 @@ public abstract class DateTimeSyntax implements Serializable, Cloneable { * Returns a hash code value for this date-time attribute. The hashcode is * that of this attribute's {@code java.util.Date} value. */ + @Override public int hashCode() { return value.hashCode(); } @@ -132,6 +134,7 @@ public abstract class DateTimeSyntax implements Serializable, Cloneable { * string value is just this attribute's {@code java.util.Date} value * converted to a string. */ + @Override public String toString() { return "" + value; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/DocAttributeSet.java b/src/java.desktop/share/classes/javax/print/attribute/DocAttributeSet.java index 102786da27b..2abd6e6322a 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/DocAttributeSet.java +++ b/src/java.desktop/share/classes/javax/print/attribute/DocAttributeSet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -59,6 +59,7 @@ public interface DocAttributeSet extends AttributeSet { * interface {@link DocAttribute DocAttribute} * @throws NullPointerException if the {@code attribute} is {@code null} */ + @Override public boolean add(Attribute attribute); /** @@ -88,5 +89,6 @@ public interface DocAttributeSet extends AttributeSet { * @throws NullPointerException if the specified set is {@code null} * @see #add(Attribute) */ + @Override public boolean addAll(AttributeSet attributes); } diff --git a/src/java.desktop/share/classes/javax/print/attribute/EnumSyntax.java b/src/java.desktop/share/classes/javax/print/attribute/EnumSyntax.java index fd48a600ee3..4c291999e02 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/EnumSyntax.java +++ b/src/java.desktop/share/classes/javax/print/attribute/EnumSyntax.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -145,6 +145,7 @@ public abstract class EnumSyntax implements Serializable, Cloneable { * semantics of enumeration values is the same object as this enumeration * value. */ + @Override public Object clone() { return this; } @@ -153,6 +154,7 @@ public abstract class EnumSyntax implements Serializable, Cloneable { * Returns a hash code value for this enumeration value. The hash code is * just this enumeration value's integer value. */ + @Override public int hashCode() { return value; } @@ -160,6 +162,7 @@ public abstract class EnumSyntax implements Serializable, Cloneable { /** * Returns a string value corresponding to this enumeration value. */ + @Override public String toString() { String[] theTable = getStringTable(); diff --git a/src/java.desktop/share/classes/javax/print/attribute/HashAttributeSet.java b/src/java.desktop/share/classes/javax/print/attribute/HashAttributeSet.java index 4ad4c8634aa..6800b45a349 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/HashAttributeSet.java +++ b/src/java.desktop/share/classes/javax/print/attribute/HashAttributeSet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -255,6 +255,7 @@ public class HashAttributeSet implements AttributeSet, Serializable { * {@link Class Class} that implements interface * {@link Attribute Attribute} */ + @Override public Attribute get(Class category) { return attrMap.get(AttributeSetUtilities. verifyAttributeCategory(category, @@ -274,6 +275,7 @@ public class HashAttributeSet implements AttributeSet, Serializable { * @throws UnmodifiableSetException if this attribute set does not support * the {@code add()} operation */ + @Override public boolean add(Attribute attribute) { Object oldAttribute = attrMap.put(attribute.getCategory(), @@ -294,6 +296,7 @@ public class HashAttributeSet implements AttributeSet, Serializable { * @throws UnmodifiableSetException if this attribute set does not support * the {@code remove()} operation */ + @Override public boolean remove(Class category) { return category != null && @@ -314,6 +317,7 @@ public class HashAttributeSet implements AttributeSet, Serializable { * @throws UnmodifiableSetException if this attribute set does not support * the {@code remove()} operation */ + @Override public boolean remove(Attribute attribute) { return attribute != null && @@ -328,6 +332,7 @@ public class HashAttributeSet implements AttributeSet, Serializable { * @return {@code true} if this attribute set contains an attribute value * for the specified category */ + @Override public boolean containsKey(Class category) { return category != null && @@ -344,6 +349,7 @@ public class HashAttributeSet implements AttributeSet, Serializable { * @return {@code true} if this attribute set contains the given attribute * value */ + @Override public boolean containsValue(Attribute attribute) { return attribute != null && attribute.equals(attrMap.get(attribute.getCategory())); @@ -371,6 +377,7 @@ public class HashAttributeSet implements AttributeSet, Serializable { * {@code null}, or the set is {@code null} * @see #add(Attribute) */ + @Override public boolean addAll(AttributeSet attributes) { Attribute []attrs = attributes.toArray(); @@ -392,6 +399,7 @@ public class HashAttributeSet implements AttributeSet, Serializable { * * @return the number of attributes in this attribute set */ + @Override public int size() { return attrMap.size(); } @@ -402,6 +410,7 @@ public class HashAttributeSet implements AttributeSet, Serializable { * @return the attributes contained in this set as an array, zero length if * the {@code AttributeSet} is empty */ + @Override public Attribute[] toArray() { Attribute []attrs = new Attribute[size()]; attrMap.values().toArray(attrs); @@ -414,6 +423,7 @@ public class HashAttributeSet implements AttributeSet, Serializable { * @throws UnmodifiableSetException if this attribute set does not support * the {@code clear()} operation */ + @Override public void clear() { attrMap.clear(); } @@ -423,6 +433,7 @@ public class HashAttributeSet implements AttributeSet, Serializable { * * @return {@code true} if this attribute set contains no attributes */ + @Override public boolean isEmpty() { return attrMap.isEmpty(); } @@ -438,6 +449,7 @@ public class HashAttributeSet implements AttributeSet, Serializable { * @return {@code true} if the specified object is equal to this attribute * set */ + @Override public boolean equals(Object object) { if (!(object instanceof AttributeSet aset)) { return false; @@ -466,6 +478,7 @@ public class HashAttributeSet implements AttributeSet, Serializable { * * @return the hash code value for this attribute set */ + @Override public int hashCode() { int hcode = 0; Attribute[] attrs = toArray(); diff --git a/src/java.desktop/share/classes/javax/print/attribute/IntegerSyntax.java b/src/java.desktop/share/classes/javax/print/attribute/IntegerSyntax.java index f6dbee3aa5a..b6846ff7271 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/IntegerSyntax.java +++ b/src/java.desktop/share/classes/javax/print/attribute/IntegerSyntax.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -107,6 +107,7 @@ public abstract class IntegerSyntax implements Serializable, Cloneable { * @return {@code true} if {@code object} is equivalent to this integer * attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return object instanceof IntegerSyntax other && value == other.value; @@ -116,6 +117,7 @@ public abstract class IntegerSyntax implements Serializable, Cloneable { * Returns a hash code value for this integer attribute. The hash code is * just this integer attribute's integer value. */ + @Override public int hashCode() { return value; } @@ -125,6 +127,7 @@ public abstract class IntegerSyntax implements Serializable, Cloneable { * string value is just this integer attribute's integer value converted to * a string. */ + @Override public String toString() { return "" + value; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/PrintJobAttributeSet.java b/src/java.desktop/share/classes/javax/print/attribute/PrintJobAttributeSet.java index 63535fba93e..ce22602f4d5 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/PrintJobAttributeSet.java +++ b/src/java.desktop/share/classes/javax/print/attribute/PrintJobAttributeSet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -59,6 +59,7 @@ public interface PrintJobAttributeSet extends AttributeSet { * interface {@link PrintJobAttribute PrintJobAttribute} * @throws NullPointerException if the {@code attribute} is {@code null} */ + @Override public boolean add(Attribute attribute); /** @@ -88,5 +89,6 @@ public interface PrintJobAttributeSet extends AttributeSet { * @throws NullPointerException if the specified set is {@code null} * @see #add(Attribute) */ + @Override public boolean addAll(AttributeSet attributes); } diff --git a/src/java.desktop/share/classes/javax/print/attribute/PrintRequestAttributeSet.java b/src/java.desktop/share/classes/javax/print/attribute/PrintRequestAttributeSet.java index 95a07655f03..958d255b296 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/PrintRequestAttributeSet.java +++ b/src/java.desktop/share/classes/javax/print/attribute/PrintRequestAttributeSet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -60,6 +60,7 @@ public interface PrintRequestAttributeSet extends AttributeSet { * interface {@link PrintRequestAttribute PrintRequestAttribute} * @throws NullPointerException if the {@code attribute} is {@code null} */ + @Override public boolean add(Attribute attribute); /** @@ -90,5 +91,6 @@ public interface PrintRequestAttributeSet extends AttributeSet { * @throws NullPointerException if the specified set is {@code null} * @see #add(Attribute) */ + @Override public boolean addAll(AttributeSet attributes); } diff --git a/src/java.desktop/share/classes/javax/print/attribute/PrintServiceAttributeSet.java b/src/java.desktop/share/classes/javax/print/attribute/PrintServiceAttributeSet.java index fd2d4dc4694..a456eac07b6 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/PrintServiceAttributeSet.java +++ b/src/java.desktop/share/classes/javax/print/attribute/PrintServiceAttributeSet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -60,6 +60,7 @@ public interface PrintServiceAttributeSet extends AttributeSet { * interface {@link PrintServiceAttribute PrintServiceAttribute} * @throws NullPointerException if the {@code attribute} is {@code null} */ + @Override public boolean add(Attribute attribute); /** @@ -90,5 +91,6 @@ public interface PrintServiceAttributeSet extends AttributeSet { * @throws NullPointerException if the specified set is {@code null} * @see #add(Attribute) */ + @Override public boolean addAll(AttributeSet attributes); } diff --git a/src/java.desktop/share/classes/javax/print/attribute/ResolutionSyntax.java b/src/java.desktop/share/classes/javax/print/attribute/ResolutionSyntax.java index 8ffae65a0d2..ffb1fab3619 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/ResolutionSyntax.java +++ b/src/java.desktop/share/classes/javax/print/attribute/ResolutionSyntax.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -266,6 +266,7 @@ public abstract class ResolutionSyntax implements Serializable, Cloneable { * @return {@code true} if {@code object} is equivalent to this resolution * attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return object instanceof ResolutionSyntax other && this.crossFeedResolution == other.crossFeedResolution && @@ -275,6 +276,7 @@ public abstract class ResolutionSyntax implements Serializable, Cloneable { /** * Returns a hash code value for this resolution attribute. */ + @Override public int hashCode() { return(((crossFeedResolution & 0x0000FFFF)) | ((feedResolution & 0x0000FFFF) << 16)); @@ -286,6 +288,7 @@ public abstract class ResolutionSyntax implements Serializable, Cloneable { * cross feed direction resolution and F is the feed direction * resolution. The values are reported in the internal units of dphi. */ + @Override public String toString() { StringBuilder result = new StringBuilder(); result.append(crossFeedResolution); diff --git a/src/java.desktop/share/classes/javax/print/attribute/SetOfIntegerSyntax.java b/src/java.desktop/share/classes/javax/print/attribute/SetOfIntegerSyntax.java index 6df67ef90ca..8088cfcd743 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/SetOfIntegerSyntax.java +++ b/src/java.desktop/share/classes/javax/print/attribute/SetOfIntegerSyntax.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -482,6 +482,7 @@ public abstract class SetOfIntegerSyntax implements Serializable, Cloneable { * @return {@code true} if {@code object} is equivalent to this * set-of-integer attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { if (object instanceof SetOfIntegerSyntax other) { int[][] myMembers = this.members; @@ -509,6 +510,7 @@ public abstract class SetOfIntegerSyntax implements Serializable, Cloneable { * code is the sum of the lower and upper bounds of the ranges in the * canonical array form, or 0 for an empty set. */ + @Override public int hashCode() { int result = 0; int n = members.length; @@ -526,6 +528,7 @@ public abstract class SetOfIntegerSyntax implements Serializable, Cloneable { * the lower bound equals the upper bound or * "i-j" otherwise. */ + @Override public String toString() { StringBuilder result = new StringBuilder(); int n = members.length; diff --git a/src/java.desktop/share/classes/javax/print/attribute/Size2DSyntax.java b/src/java.desktop/share/classes/javax/print/attribute/Size2DSyntax.java index 9ff772bc30d..056031b52f2 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/Size2DSyntax.java +++ b/src/java.desktop/share/classes/javax/print/attribute/Size2DSyntax.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -263,6 +263,7 @@ public abstract class Size2DSyntax implements Serializable, Cloneable { * @return {@code true} if {@code object} is equivalent to this * two-dimensional size attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return object instanceof Size2DSyntax size2DSyntax && this.x == size2DSyntax.x && @@ -272,6 +273,7 @@ public abstract class Size2DSyntax implements Serializable, Cloneable { /** * Returns a hash code value for this two-dimensional size attribute. */ + @Override public int hashCode() { return (((x & 0x0000FFFF) ) | ((y & 0x0000FFFF) << 16)); @@ -283,6 +285,7 @@ public abstract class Size2DSyntax implements Serializable, Cloneable { * is the {@code X} dimension and Y is the {@code Y} dimension. The * values are reported in the internal units of micrometers. */ + @Override public String toString() { StringBuilder result = new StringBuilder(); result.append(x); diff --git a/src/java.desktop/share/classes/javax/print/attribute/TextSyntax.java b/src/java.desktop/share/classes/javax/print/attribute/TextSyntax.java index 9a343bc8af2..2c49a11b250 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/TextSyntax.java +++ b/src/java.desktop/share/classes/javax/print/attribute/TextSyntax.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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,6 +112,7 @@ public abstract class TextSyntax implements Serializable, Cloneable { * * @return a hashcode value for this object */ + @Override public int hashCode() { return value.hashCode() ^ locale.hashCode(); } @@ -131,6 +132,7 @@ public abstract class TextSyntax implements Serializable, Cloneable { * @return {@code true} if {@code object} is equivalent to this text * attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return object instanceof TextSyntax other && this.value.equals(other.value) && @@ -143,6 +145,7 @@ public abstract class TextSyntax implements Serializable, Cloneable { * * @return a {@code String} identifying this object */ + @Override public String toString(){ return value; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/URISyntax.java b/src/java.desktop/share/classes/javax/print/attribute/URISyntax.java index 10545df71fd..b3e604283f7 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/URISyntax.java +++ b/src/java.desktop/share/classes/javax/print/attribute/URISyntax.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -82,6 +82,7 @@ public abstract class URISyntax implements Serializable, Cloneable { * * @return a hashcode value for this object */ + @Override public int hashCode() { return uri.hashCode(); } @@ -100,6 +101,7 @@ public abstract class URISyntax implements Serializable, Cloneable { * @return {@code true} if {@code object} is equivalent to this {@code URI} * attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return object instanceof URISyntax other && this.uri.equals(other.uri); @@ -112,6 +114,7 @@ public abstract class URISyntax implements Serializable, Cloneable { * * @return a {@code String} identifying this object */ + @Override public String toString() { return uri.toString(); } diff --git a/src/java.desktop/share/classes/javax/swing/AbstractButton.java b/src/java.desktop/share/classes/javax/swing/AbstractButton.java index a1961acce29..ad5f0eba3de 100644 --- a/src/java.desktop/share/classes/javax/swing/AbstractButton.java +++ b/src/java.desktop/share/classes/javax/swing/AbstractButton.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 diff --git a/src/java.desktop/share/classes/javax/swing/ActionPropertyChangeListener.java b/src/java.desktop/share/classes/javax/swing/ActionPropertyChangeListener.java index 19b9152a1b5..7d33e936b7b 100644 --- a/src/java.desktop/share/classes/javax/swing/ActionPropertyChangeListener.java +++ b/src/java.desktop/share/classes/javax/swing/ActionPropertyChangeListener.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, 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 diff --git a/src/java.desktop/share/classes/javax/swing/AncestorNotifier.java b/src/java.desktop/share/classes/javax/swing/AncestorNotifier.java index a3ed463d2d5..3606bdc9c0b 100644 --- a/src/java.desktop/share/classes/javax/swing/AncestorNotifier.java +++ b/src/java.desktop/share/classes/javax/swing/AncestorNotifier.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 diff --git a/src/java.desktop/share/classes/javax/swing/ArrayTable.java b/src/java.desktop/share/classes/javax/swing/ArrayTable.java index 282bd3454ca..4941264c0fe 100644 --- a/src/java.desktop/share/classes/javax/swing/ArrayTable.java +++ b/src/java.desktop/share/classes/javax/swing/ArrayTable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -148,7 +148,7 @@ class ArrayTable implements Cloneable { if (table != null) { if (isArray()) { Object[] array = (Object[])table; - for (int i = 0; i loadSystemClass(String className) throws ClassNotFoundException { return Class.forName(className, true, Thread.currentThread(). getContextClassLoader()); diff --git a/src/java.desktop/share/classes/javax/swing/SwingWorker.java b/src/java.desktop/share/classes/javax/swing/SwingWorker.java index 75f1700bded..dae695b4868 100644 --- a/src/java.desktop/share/classes/javax/swing/SwingWorker.java +++ b/src/java.desktop/share/classes/javax/swing/SwingWorker.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, 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 @@ -45,7 +45,8 @@ import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import sun.awt.AppContext; +import sun.awt.util.ThreadGroupUtils; + import sun.swing.AccumulativeRunnable; /** @@ -266,7 +267,7 @@ public abstract class SwingWorker implements RunnableFuture { */ private AccumulativeRunnable doNotifyProgressChange; - private final AccumulativeRunnable doSubmit = getDoSubmit(); + private final AccumulativeRunnable doSubmit = new DoSubmitAccumulativeRunnable(); /** * Values for the {@code state} bound property. @@ -755,18 +756,16 @@ public abstract class SwingWorker implements RunnableFuture { } + private static ExecutorService executorService; + /** * returns workersExecutorService. * - * returns the service stored in the appContext or creates it if - * necessary. + * returns the service and creates it if necessary. * * @return ExecutorService for the {@code SwingWorkers} */ private static synchronized ExecutorService getWorkersExecutorService() { - final AppContext appContext = AppContext.getAppContext(); - ExecutorService executorService = - (ExecutorService) appContext.get(SwingWorker.class); if (executorService == null) { //this creates daemon threads. ThreadFactory threadFactory = @@ -788,46 +787,26 @@ public abstract class SwingWorker implements RunnableFuture { 10L, TimeUnit.MINUTES, new LinkedBlockingQueue(), threadFactory); - appContext.put(SwingWorker.class, executorService); - // Don't use ShutdownHook here as it's not enough. We should track - // AppContext disposal instead of JVM shutdown, see 6799345 for details - final ExecutorService es = executorService; - appContext.addPropertyChangeListener(AppContext.DISPOSED_PROPERTY_NAME, - new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent pce) { - boolean disposed = (Boolean)pce.getNewValue(); - if (disposed) { - final WeakReference executorServiceRef = - new WeakReference(es); - final ExecutorService executorService = - executorServiceRef.get(); - if (executorService != null) { - executorService.shutdown(); - } - } - } + final Runnable shutdownHook = new Runnable() { + final WeakReference executorServiceRef = + new WeakReference(executorService); + public void run() { + final ExecutorService executorService = executorServiceRef.get(); + if (executorService != null) { + executorService.shutdown(); + } } - ); + }; + ThreadGroup rootTG = ThreadGroupUtils.getRootThreadGroup(); + Thread t = new Thread(rootTG, shutdownHook, + "SwingWorker ES", 0, false); + t.setContextClassLoader(null); + Runtime.getRuntime().addShutdownHook(t); } return executorService; } - private static final Object DO_SUBMIT_KEY = new StringBuilder("doSubmit"); - private static AccumulativeRunnable getDoSubmit() { - synchronized (DO_SUBMIT_KEY) { - final AppContext appContext = AppContext.getAppContext(); - Object doSubmit = appContext.get(DO_SUBMIT_KEY); - if (doSubmit == null) { - doSubmit = new DoSubmitAccumulativeRunnable(); - appContext.put(DO_SUBMIT_KEY, doSubmit); - } - @SuppressWarnings("unchecked") - AccumulativeRunnable tmp = (AccumulativeRunnable) doSubmit; - return tmp; - } - } private static class DoSubmitAccumulativeRunnable extends AccumulativeRunnable implements ActionListener { private static final int DELAY = 1000 / 30; diff --git a/src/java.desktop/share/classes/javax/swing/Timer.java b/src/java.desktop/share/classes/javax/swing/Timer.java index 2cb8381d7d3..1063532715c 100644 --- a/src/java.desktop/share/classes/javax/swing/Timer.java +++ b/src/java.desktop/share/classes/javax/swing/Timer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -173,8 +173,7 @@ public class Timer implements Serializable private final transient Lock lock = new ReentrantLock(); // This field is maintained by TimerQueue. - // eventQueued can also be reset by the TimerQueue, but will only ever - // happen in an AppContext case when TimerQueues thread is destroyed. + // eventQueued can also be reset by the TimerQueue // access to this field is synchronized on getLock() lock. transient TimerQueue.DelayedTimer delayedTimer = null; diff --git a/src/java.desktop/share/classes/javax/swing/UIDefaults.java b/src/java.desktop/share/classes/javax/swing/UIDefaults.java index 67a739360f9..1d6b7257273 100644 --- a/src/java.desktop/share/classes/javax/swing/UIDefaults.java +++ b/src/java.desktop/share/classes/javax/swing/UIDefaults.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -1171,7 +1171,7 @@ public class UIDefaults extends Hashtable Class[] types = null; if (args != null) { types = new Class[args.length]; - for (int i = 0; i< args.length; i++) { + for (int i = 0; i < args.length; i++) { /* PENDING(ges): At present only the primitive types used are handled correctly; this should eventually handle all primitive types */ diff --git a/src/java.desktop/share/classes/javax/swing/UIManager.java b/src/java.desktop/share/classes/javax/swing/UIManager.java index 69063c562e6..f323842ae49 100644 --- a/src/java.desktop/share/classes/javax/swing/UIManager.java +++ b/src/java.desktop/share/classes/javax/swing/UIManager.java @@ -56,7 +56,6 @@ import sun.awt.OSInfo; import sun.swing.SwingUtilities2; import java.util.HashMap; import java.util.Objects; -import sun.awt.AppContext; import sun.awt.AWTAccessor; import sun.swing.SwingAccessor; @@ -179,10 +178,7 @@ public class UIManager implements Serializable /** * This class defines the state managed by the UIManager. For * Swing applications the fields in this class could just as well - * be static members of UIManager however we give them - * "AppContext" - * scope instead so that potentially multiple lightweight - * applications running in a single VM have their own state. + * be static members of UIManager. */ private static class LAFState { @@ -206,8 +202,8 @@ public class UIManager implements Serializable void setSystemDefaults(UIDefaults x) { tables[1] = x; } /** - * Returns the SwingPropertyChangeSupport for the current - * AppContext. If create is a true, a non-null + * Returns the SwingPropertyChangeSupport instance. + * If create is a true, a non-null * SwingPropertyChangeSupport will be returned, if * create is false and this has not been invoked * with true, null will be returned. @@ -1366,18 +1362,7 @@ public class UIManager implements Serializable return; } - // Try to get default LAF from system property, then from AppContext - // (6653395), then use cross-platform one by default. - String lafName = null; - @SuppressWarnings("unchecked") - HashMap lafData = - (HashMap) AppContext.getAppContext().remove("swing.lafdata"); - if (lafData != null) { - lafName = lafData.remove("defaultlaf"); - } - if (lafName == null) { - lafName = getCrossPlatformLookAndFeelClassName(); - } + String lafName = getCrossPlatformLookAndFeelClassName(); lafName = swingProps.getProperty(defaultLAFKey, lafName); try { @@ -1385,13 +1370,6 @@ public class UIManager implements Serializable } catch (Exception e) { throw new Error("Cannot load " + lafName); } - - // Set any properties passed through AppContext (6653395). - if (lafData != null) { - for (Object key: lafData.keySet()) { - UIManager.put(key, lafData.get(key)); - } - } } @@ -1451,8 +1429,8 @@ public class UIManager implements Serializable /* * This method is called before any code that depends on the - * AppContext specific LAFState object runs. - * In some AppContext cases, it's possible for this method + * LAFState object runs. + * In some cases, it's possible for this method * to be re-entered, which is why we grab a lock before calling * initialize(). */ diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicComboPopup.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicComboPopup.java index 42791772c2d..d825dd73e9c 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicComboPopup.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicComboPopup.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, 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 @@ -243,8 +243,8 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup { public void hide() { MenuSelectionManager manager = MenuSelectionManager.defaultManager(); MenuElement [] selection = manager.getSelectedPath(); - for ( int i = 0 ; i < selection.length ; i++ ) { - if ( selection[i] == this ) { + for (int i = 0 ; i < selection.length; i++ ) { + if (selection[i] == this) { manager.clearSelectedPath(); break; } @@ -924,7 +924,7 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup { if (!SwingUtilities.isLeftMouseButton(e) || !comboBox.isEnabled() || !comboBox.isShowing()) return; - if ( comboBox.isEditable() ) { + if (comboBox.isEditable()) { Component comp = comboBox.getEditor().getEditorComponent(); if ((!(comp instanceof JComponent)) || ((JComponent)comp).isRequestFocusEnabled()) { comp.requestFocus(FocusEvent.Cause.MOUSE_EVENT); @@ -957,12 +957,12 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup { Component source = (Component)e.getSource(); Dimension size = source.getSize(); Rectangle bounds = new Rectangle( 0, 0, size.width, size.height); - if ( !bounds.contains( e.getPoint() ) ) { + if (!bounds.contains(e.getPoint())) { MouseEvent newEvent = convertMouseEvent( e ); Point location = newEvent.getPoint(); Rectangle r = new Rectangle(); list.computeVisibleRect( r ); - if ( r.contains( location ) ) { + if (r.contains(location)) { if (comboBox.getSelectedIndex() == list.getSelectedIndex()) { comboBox.getEditor().setItem(list.getSelectedValue()); } @@ -989,7 +989,7 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup { Point location = anEvent.getPoint(); Rectangle r = new Rectangle(); list.computeVisibleRect( r ); - if ( r.contains( location ) ) { + if (r.contains(location)) { updateListBoxSelectionForEvent( anEvent, false ); } } @@ -999,34 +999,34 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup { if (e.getSource() == list) { return; } - if ( isVisible() ) { + if (isVisible()) { MouseEvent newEvent = convertMouseEvent( e ); Rectangle r = new Rectangle(); list.computeVisibleRect( r ); - if ( newEvent.getPoint().y >= r.y && newEvent.getPoint().y <= r.y + r.height - 1 ) { + if (newEvent.getPoint().y >= r.y && newEvent.getPoint().y <= r.y + r.height - 1) { hasEntered = true; - if ( isAutoScrolling ) { + if (isAutoScrolling) { stopAutoScrolling(); } Point location = newEvent.getPoint(); - if ( r.contains( location ) ) { + if (r.contains(location)) { updateListBoxSelectionForEvent( newEvent, false ); } } else { - if ( hasEntered ) { + if (hasEntered) { int directionToScroll = newEvent.getPoint().y < r.y ? SCROLL_UP : SCROLL_DOWN; - if ( isAutoScrolling && scrollDirection != directionToScroll ) { + if (isAutoScrolling && scrollDirection != directionToScroll) { stopAutoScrolling(); startAutoScrolling( directionToScroll ); } - else if ( !isAutoScrolling ) { + else if (!isAutoScrolling) { startAutoScrolling( directionToScroll ); } } else { - if ( e.getPoint().y < 0 ) { + if (e.getPoint().y < 0) { hasEntered = true; startAutoScrolling( SCROLL_UP ); } @@ -1043,7 +1043,7 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup { JComboBox comboBox = (JComboBox)e.getSource(); String propertyName = e.getPropertyName(); - if ( propertyName == "model" ) { + if (propertyName == "model") { @SuppressWarnings("unchecked") ComboBoxModel oldModel = (ComboBoxModel)e.getOldValue(); @SuppressWarnings("unchecked") @@ -1053,13 +1053,13 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup { list.setModel(newModel); - if ( isVisible() ) { + if (isVisible()) { hide(); } } - else if ( propertyName == "renderer" ) { + else if (propertyName == "renderer") { list.setCellRenderer( comboBox.getRenderer() ); - if ( isVisible() ) { + if (isVisible()) { hide(); } } @@ -1067,18 +1067,18 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup { // Pass along the new component orientation // to the list and the scroller - ComponentOrientation o =(ComponentOrientation)e.getNewValue(); + ComponentOrientation o = (ComponentOrientation)e.getNewValue(); JList list = getList(); - if (list != null && list.getComponentOrientation()!=o) { + if (list != null && list.getComponentOrientation() != o) { list.setComponentOrientation(o); } - if (scroller != null && scroller.getComponentOrientation()!=o) { + if (scroller != null && scroller.getComponentOrientation() != o) { scroller.setComponentOrientation(o); } - if (o!=getComponentOrientation()) { + if (o != getComponentOrientation()) { setComponentOrientation(o); } } @@ -1134,13 +1134,13 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup { protected void startAutoScrolling( int direction ) { // XXX - should be a private method within InvocationMouseMotionHandler // if possible. - if ( isAutoScrolling ) { + if (isAutoScrolling) { autoscrollTimer.stop(); } isAutoScrolling = true; - if ( direction == SCROLL_UP ) { + if (direction == SCROLL_UP) { scrollDirection = SCROLL_UP; Point convertedPoint = SwingUtilities.convertPoint( scroller, new Point( 1, 1 ), list ); int top = list.locationToIndex( convertedPoint ); @@ -1149,7 +1149,7 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup { autoscrollTimer = new Timer( 100, new AutoScrollActionHandler( SCROLL_UP) ); } - else if ( direction == SCROLL_DOWN ) { + else if (direction == SCROLL_DOWN) { scrollDirection = SCROLL_DOWN; Dimension size = scroller.getSize(); Point convertedPoint = SwingUtilities.convertPoint( scroller, @@ -1171,7 +1171,7 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup { protected void stopAutoScrolling() { isAutoScrolling = false; - if ( autoscrollTimer != null ) { + if (autoscrollTimer != null) { autoscrollTimer.stop(); autoscrollTimer = null; } @@ -1183,7 +1183,7 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup { */ protected void autoScrollUp() { int index = list.getSelectedIndex(); - if ( index > 0 ) { + if (index > 0) { list.setSelectedIndex( index - 1 ); list.ensureIndexIsVisible( index - 1 ); } @@ -1196,7 +1196,7 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup { protected void autoScrollDown() { int index = list.getSelectedIndex(); int lastItem = list.getModel().getSize() - 1; - if ( index < lastItem ) { + if (index < lastItem) { list.setSelectedIndex( index + 1 ); list.ensureIndexIsVisible( index + 1 ); } @@ -1234,7 +1234,7 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup { * @param e a mouse event */ protected void delegateFocus( MouseEvent e ) { - if ( comboBox.isEditable() ) { + if (comboBox.isEditable()) { Component comp = comboBox.getEditor().getEditorComponent(); if ((!(comp instanceof JComponent)) || ((JComponent)comp).isRequestFocusEnabled()) { if (e != null) { @@ -1258,7 +1258,7 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup { * visible. */ protected void togglePopup() { - if ( isVisible() ) { + if (isVisible()) { hide(); } else { @@ -1274,7 +1274,7 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup { * @param selectedIndex the index to set the list */ private void setListSelection(int selectedIndex) { - if ( selectedIndex == -1 ) { + if (selectedIndex == -1) { list.clearSelection(); } else { @@ -1325,7 +1325,7 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup { ListCellRenderer renderer = list.getCellRenderer(); Object value = null; - for ( int i = 0; i < minRowCount; ++i ) { + for (int i = 0; i < minRowCount; ++i) { value = list.getModel().getElementAt( i ); Component c = renderer.getListCellRendererComponent( list, value, i, false, false ); height += c.getPreferredSize().height; @@ -1439,18 +1439,18 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup { // XXX - only seems to be called from this class. shouldScroll flag is // never true Point location = anEvent.getPoint(); - if ( list == null ) + if (list == null) return; int index = list.locationToIndex(location); - if ( index == -1 ) { - if ( location.y < 0 ) + if (index == -1) { + if (location.y < 0) index = 0; else index = comboBox.getModel().getSize() - 1; } - if ( list.getSelectedIndex() != index ) { + if (list.getSelectedIndex() != index) { list.setSelectedIndex(index); - if ( shouldScroll ) + if (shouldScroll) list.ensureIndexIsVisible(index); } } diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicListUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicListUI.java index ab5fdb12c5a..37bcbec2156 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicListUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicListUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicMenuBarUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicMenuBarUI.java index b7310b56aef..a9abaee0129 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicMenuBarUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicMenuBarUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -237,10 +237,10 @@ public class BasicMenuBarUI extends MenuBarUI { // ChangeListener // public void stateChanged(ChangeEvent e) { - int i,c; - for(i=0,c = menuBar.getMenuCount() ; i < c ; i++) { + final int c = menuBar.getMenuCount(); + for (int i = 0; i < c; i++) { JMenu menu = menuBar.getMenu(i); - if(menu != null && menu.isSelected()) { + if (menu != null && menu.isSelected()) { menuBar.getSelectionModel().setSelectedIndex(i); break; } diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicPopupMenuUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicPopupMenuUI.java index b72a2d8a140..a3ffb034b4b 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicPopupMenuUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicPopupMenuUI.java @@ -908,7 +908,7 @@ public class BasicPopupMenuUI extends PopupMenuUI { } boolean isInPopup(Component src) { - for (Component c=src; c != null; c=c.getParent()) { + for (Component c = src; c != null; c = c.getParent()) { if (c instanceof Window) { break; } else if (c instanceof JPopupMenu) { diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicSpinnerUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicSpinnerUI.java index 7080fa3aac1..b523f8c7bd3 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicSpinnerUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicSpinnerUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTabbedPaneUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTabbedPaneUI.java index 6e0d0101b9c..de23dbab29a 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTabbedPaneUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTabbedPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -504,7 +504,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants { tabPane.addFocusListener(focusListener); } tabPane.addContainerListener(getHandler()); - if (tabPane.getTabCount()>0) { + if (tabPane.getTabCount() > 0) { Boolean htmlDisabled = (Boolean) tabPane.getClientProperty("html.disable"); if (!(Boolean.TRUE.equals(htmlDisabled))) { @@ -949,8 +949,8 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants { // Paint tabRuns of tabs from back to front for (int i = runCount - 1; i >= 0; i--) { int start = tabRuns[i]; - int next = tabRuns[(i == runCount - 1)? 0 : i + 1]; - int end = (next != 0? next - 1: tabCount - 1); + int next = tabRuns[(i == runCount - 1) ? 0 : i + 1]; + int end = (next != 0 ? next - 1 : tabCount - 1); for (int j = start; j <= end; j++) { if (j != selectedIndex && rects[j].intersects(clipRect)) { paintTab(g, tabPlacement, rects, j, iconRect, textRect); @@ -1118,7 +1118,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants { int xx = x; g.setColor(shadow); while(xx <= x+rects[tabIndex].width) { - for (int i=0; i < xCropLen.length; i+=2) { + for (int i = 0; i < xCropLen.length; i += 2) { g.drawLine(xx+yCropLen[i],y-xCropLen[i], xx+yCropLen[i+1]-1,y-xCropLen[i+1]); } @@ -1133,7 +1133,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants { int yy = y; g.setColor(shadow); while(yy <= y+rects[tabIndex].height) { - for (int i=0; i < xCropLen.length; i+=2) { + for (int i = 0; i < xCropLen.length; i += 2) { g.drawLine(x-xCropLen[i],yy+yCropLen[i], x-xCropLen[i+1],yy+yCropLen[i+1]-1); } @@ -1549,7 +1549,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants { protected void paintContentBorderTopEdge(Graphics g, int tabPlacement, int selectedIndex, int x, int y, int w, int h) { - Rectangle selRect = selectedIndex < 0? null : + Rectangle selRect = selectedIndex < 0 ? null : getTabBounds(selectedIndex, calcRect); g.setColor(lightHighlight); @@ -1588,7 +1588,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants { protected void paintContentBorderLeftEdge(Graphics g, int tabPlacement, int selectedIndex, int x, int y, int w, int h) { - Rectangle selRect = selectedIndex < 0? null : + Rectangle selRect = selectedIndex < 0 ? null : getTabBounds(selectedIndex, calcRect); g.setColor(lightHighlight); @@ -1624,7 +1624,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants { protected void paintContentBorderBottomEdge(Graphics g, int tabPlacement, int selectedIndex, int x, int y, int w, int h) { - Rectangle selRect = selectedIndex < 0? null : + Rectangle selRect = selectedIndex < 0 ? null : getTabBounds(selectedIndex, calcRect); g.setColor(shadow); @@ -1667,7 +1667,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants { protected void paintContentBorderRightEdge(Graphics g, int tabPlacement, int selectedIndex, int x, int y, int w, int h) { - Rectangle selRect = selectedIndex < 0? null : + Rectangle selRect = selectedIndex < 0 ? null : getTabBounds(selectedIndex, calcRect); g.setColor(shadow); @@ -4090,7 +4090,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants { setHtmlView(v, inserted, index); } } else { // Not HTML - if (htmlViews != null) { // Add placeholder + if (htmlViews != null) { // Add placeholder setHtmlView(null, inserted, index); } // else nada! } @@ -4336,8 +4336,8 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants { private Vector createHTMLVector() { Vector htmlViews = new Vector(); int count = tabPane.getTabCount(); - if (count>0) { - for (int i=0 ; i 0) { + for (int i = 0 ; i < count; i++) { String title = tabPane.getTitleAt(i); if (BasicHTML.isHTMLString(title)) { htmlViews.addElement(BasicHTML.createHTMLView(tabPane, title)); diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicToolBarUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicToolBarUI.java index 1bcd4a9be8d..9c07b6a03d1 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicToolBarUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicToolBarUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTreeUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTreeUI.java index 096fe7cc5f7..19a25005be9 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTreeUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTreeUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 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 7c56c681423..461c597cc5f 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 @@ -706,6 +706,11 @@ public class MetalLookAndFeel extends BasicLookAndFeel // DEFAULTS TABLE // + Object commonInputMap = new UIDefaults.LazyInputMap(new Object[] { + "SPACE", "pressed", + "released SPACE", "released" + }); + Object[] defaults = { // *** Auditory Feedback "AuditoryCues.defaultCueList", defaultCueList, @@ -791,6 +796,8 @@ public class MetalLookAndFeel extends BasicLookAndFeel }), + + // Buttons "Button.defaultButtonFollowsFocus", Boolean.FALSE, "Button.disabledText", inactiveControlTextColor, @@ -798,10 +805,8 @@ public class MetalLookAndFeel extends BasicLookAndFeel "Button.border", buttonBorder, "Button.font", controlTextValue, "Button.focus", focusColor, - "Button.focusInputMap", new UIDefaults.LazyInputMap(new Object[] { - "SPACE", "pressed", - "released SPACE", "released" - }), + "Button.focusInputMap", commonInputMap, + // Button default margin is (2, 14, 2, 14), defined in // BasicLookAndFeel via "Button.margin" UI property. @@ -810,11 +815,8 @@ public class MetalLookAndFeel extends BasicLookAndFeel "CheckBox.font", controlTextValue, "CheckBox.focus", focusColor, "CheckBox.icon",(LazyValue) t -> MetalIconFactory.getCheckBoxIcon(), - "CheckBox.focusInputMap", - new UIDefaults.LazyInputMap(new Object[] { - "SPACE", "pressed", - "released SPACE", "released" - }), + "CheckBox.focusInputMap", commonInputMap, + // margin is 2 all the way around, BasicBorders.RadioButtonBorder // (checkbox uses RadioButtonBorder) is 2 all the way around too. "CheckBox.totalInsets", new Insets(4, 4, 4, 4), @@ -824,11 +826,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel "RadioButton.icon",(LazyValue) t -> MetalIconFactory.getRadioButtonIcon(), "RadioButton.font", controlTextValue, "RadioButton.focus", focusColor, - "RadioButton.focusInputMap", - new UIDefaults.LazyInputMap(new Object[] { - "SPACE", "pressed", - "released SPACE", "released" - }), + "RadioButton.focusInputMap", commonInputMap, // margin is 2 all the way around, BasicBorders.RadioButtonBorder // is 2 all the way around too. "RadioButton.totalInsets", new Insets(4, 4, 4, 4), @@ -838,11 +836,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel "ToggleButton.focus", focusColor, "ToggleButton.border", toggleButtonBorder, "ToggleButton.font", controlTextValue, - "ToggleButton.focusInputMap", - new UIDefaults.LazyInputMap(new Object[] { - "SPACE", "pressed", - "released SPACE", "released" - }), + "ToggleButton.focusInputMap", commonInputMap, // File View diff --git a/src/java.desktop/share/classes/javax/swing/plaf/nimbus/AbstractRegionPainter.java b/src/java.desktop/share/classes/javax/swing/plaf/nimbus/AbstractRegionPainter.java index cfcc014940a..d06406d69d6 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/nimbus/AbstractRegionPainter.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/nimbus/AbstractRegionPainter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, 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 diff --git a/src/java.desktop/share/classes/javax/swing/plaf/nimbus/NimbusLookAndFeel.java b/src/java.desktop/share/classes/javax/swing/plaf/nimbus/NimbusLookAndFeel.java index 05fa3bbc9b6..7ef7beb5d1c 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/nimbus/NimbusLookAndFeel.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/nimbus/NimbusLookAndFeel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, 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 diff --git a/src/java.desktop/share/classes/javax/swing/plaf/nimbus/SynthPainterImpl.java b/src/java.desktop/share/classes/javax/swing/plaf/nimbus/SynthPainterImpl.java index 1842073588b..fd761ac4730 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/nimbus/SynthPainterImpl.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/nimbus/SynthPainterImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, 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 diff --git a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthComboBoxUI.java b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthComboBoxUI.java index ad10e70a837..0c373483153 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthComboBoxUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthComboBoxUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2026, 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 diff --git a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthScrollPaneUI.java b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthScrollPaneUI.java index 0c3a17fbdbb..c07feb8b56d 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthScrollPaneUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthScrollPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2026, 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 diff --git a/src/java.desktop/share/classes/javax/swing/text/TextAction.java b/src/java.desktop/share/classes/javax/swing/text/TextAction.java index d05f7e24a6b..00da7bad93e 100644 --- a/src/java.desktop/share/classes/javax/swing/text/TextAction.java +++ b/src/java.desktop/share/classes/javax/swing/text/TextAction.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 diff --git a/src/java.desktop/share/classes/javax/swing/tree/DefaultTreeCellEditor.java b/src/java.desktop/share/classes/javax/swing/tree/DefaultTreeCellEditor.java index 3f3721dd30a..7595638dc66 100644 --- a/src/java.desktop/share/classes/javax/swing/tree/DefaultTreeCellEditor.java +++ b/src/java.desktop/share/classes/javax/swing/tree/DefaultTreeCellEditor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, 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 diff --git a/src/java.desktop/share/classes/module-info.java b/src/java.desktop/share/classes/module-info.java index 6d34d934194..57392f71321 100644 --- a/src/java.desktop/share/classes/module-info.java +++ b/src/java.desktop/share/classes/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -115,7 +115,6 @@ module java.desktop { // qualified exports may be inserted at build time // see make/GensrcModuleInfo.gmk exports sun.awt to - jdk.accessibility, jdk.unsupported.desktop; exports java.awt.dnd.peer to jdk.unsupported.desktop; diff --git a/src/java.desktop/share/classes/sun/awt/AWTAccessor.java b/src/java.desktop/share/classes/sun/awt/AWTAccessor.java index f98118171b4..143d7e6198c 100644 --- a/src/java.desktop/share/classes/sun/awt/AWTAccessor.java +++ b/src/java.desktop/share/classes/sun/awt/AWTAccessor.java @@ -755,8 +755,6 @@ public final class AWTAccessor { * An accessor object for the AccessibleContext class */ public interface AccessibleContextAccessor { - void setAppContext(AccessibleContext accessibleContext, AppContext appContext); - AppContext getAppContext(AccessibleContext accessibleContext); Object getNativeAXResource(AccessibleContext accessibleContext); void setNativeAXResource(AccessibleContext accessibleContext, Object value); } diff --git a/src/java.desktop/share/classes/sun/awt/EmbeddedFrame.java b/src/java.desktop/share/classes/sun/awt/EmbeddedFrame.java index ab2ad5dfbf0..fa8ed1e707c 100644 --- a/src/java.desktop/share/classes/sun/awt/EmbeddedFrame.java +++ b/src/java.desktop/share/classes/sun/awt/EmbeddedFrame.java @@ -164,11 +164,8 @@ public abstract class EmbeddedFrame extends Frame } /** - * Because there may be many AppContexts, and we can't be sure where this - * EmbeddedFrame is first created or shown, we can't automatically determine - * the correct KeyboardFocusManager to attach to as KeyEventDispatcher. * Those who want to use the functionality of traversing out of the EmbeddedFrame - * must call this method on the AppContext. After that, all the changes + * must call this method. After that, all the changes * can be handled automatically, including possible replacement of * KeyboardFocusManager. */ @@ -184,7 +181,7 @@ public abstract class EmbeddedFrame extends Frame /** * Needed to avoid memory leak: we register this EmbeddedFrame as a listener with - * KeyboardFocusManager of an AppContext. We don't want the KFM to keep + * the KeyboardFocusManager. We don't want the KFM to keep * reference to our EmbeddedFrame forever if the Frame is no longer in use, so we * add listeners in show() and remove them in hide(). */ @@ -198,7 +195,7 @@ public abstract class EmbeddedFrame extends Frame /** * Needed to avoid memory leak: we register this EmbeddedFrame as a listener with - * KeyboardFocusManager of an AppContext. We don't want the KFM to keep + * the KeyboardFocusManager. We don't want the KFM to keep * reference to our EmbeddedFrame forever if the Frame is no longer in use, so we * add listeners in show() and remove them in hide(). */ diff --git a/src/java.desktop/share/classes/sun/awt/GlobalCursorManager.java b/src/java.desktop/share/classes/sun/awt/GlobalCursorManager.java index 27893d0ce87..8638bf81921 100644 --- a/src/java.desktop/share/classes/sun/awt/GlobalCursorManager.java +++ b/src/java.desktop/share/classes/sun/awt/GlobalCursorManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, 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 @@ -61,7 +61,7 @@ public abstract class GlobalCursorManager { } } if (shouldPost) { - SunToolkit.postEvent(SunToolkit.targetToAppContext(heavy), in); + SunToolkit.postEvent(in); } } } diff --git a/src/java.desktop/share/classes/sun/awt/KeyboardFocusManagerPeerImpl.java b/src/java.desktop/share/classes/sun/awt/KeyboardFocusManagerPeerImpl.java index ef50d883ee5..909c0b58136 100644 --- a/src/java.desktop/share/classes/sun/awt/KeyboardFocusManagerPeerImpl.java +++ b/src/java.desktop/share/classes/sun/awt/KeyboardFocusManagerPeerImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -127,7 +127,7 @@ public abstract class KeyboardFocusManagerPeerImpl implements KeyboardFocusManag if (focusLog.isLoggable(PlatformLogger.Level.FINER)) { focusLog.finer("Posting focus event: " + fl); } - SunToolkit.postEvent(SunToolkit.targetToAppContext(currentOwner), fl); + SunToolkit.postEvent(fl); } FocusEvent fg = new FocusEvent(lightweightChild, FocusEvent.FOCUS_GAINED, @@ -136,7 +136,7 @@ public abstract class KeyboardFocusManagerPeerImpl implements KeyboardFocusManag if (focusLog.isLoggable(PlatformLogger.Level.FINER)) { focusLog.finer("Posting focus event: " + fg); } - SunToolkit.postEvent(SunToolkit.targetToAppContext(lightweightChild), fg); + SunToolkit.postEvent(fg); return true; } diff --git a/src/java.desktop/share/classes/sun/awt/PaintEventDispatcher.java b/src/java.desktop/share/classes/sun/awt/PaintEventDispatcher.java index eec1fc93c66..831c67e1e4f 100644 --- a/src/java.desktop/share/classes/sun/awt/PaintEventDispatcher.java +++ b/src/java.desktop/share/classes/sun/awt/PaintEventDispatcher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, 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 @@ -93,7 +93,7 @@ public class PaintEventDispatcher { * This method is invoked from the toolkit thread when the surface * data of the component needs to be replaced. The method run() of * the Runnable argument performs surface data replacing, run() - * should be invoked on the EDT of this component's AppContext. + * should be invoked on the EDT. * Returns true if the Runnable has been enqueued to be invoked * on the EDT. * (Fix 6255371.) diff --git a/src/java.desktop/share/classes/sun/awt/util/PerformanceLogger.java b/src/java.desktop/share/classes/sun/awt/util/PerformanceLogger.java index 4a1dde6538d..cf0ff1d9958 100644 --- a/src/java.desktop/share/classes/sun/awt/util/PerformanceLogger.java +++ b/src/java.desktop/share/classes/sun/awt/util/PerformanceLogger.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2026, 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 @@ -43,11 +43,6 @@ import java.io.Writer; * for setting and getting all times and for doing whatever * analysis is interesting; this class is merely a central container * for those timing values. - * Note that, due to the variables in this class being static, - * use of particular time values by multiple AppContexts will cause - * confusing results. For example, if two contexts run - * simultaneously, the initTime for those will collide - * and the results may be undefined. *

* To automatically track startup performance in an app * use the command-line parameter sun.perflog as follows:
diff --git a/src/java.desktop/share/classes/sun/font/ExtendedTextSourceLabel.java b/src/java.desktop/share/classes/sun/font/ExtendedTextSourceLabel.java index f3569941321..78ae70629b6 100644 --- a/src/java.desktop/share/classes/sun/font/ExtendedTextSourceLabel.java +++ b/src/java.desktop/share/classes/sun/font/ExtendedTextSourceLabel.java @@ -57,15 +57,16 @@ import java.util.Map; * Align bounds is a rect that defines how to align this to margins. * it generally allows some overhang that logical bounds would prevent. */ -class ExtendedTextSourceLabel implements TextLineComponent, Decoration.Label { +final class ExtendedTextSourceLabel implements TextLineComponent, Decoration.Label { private final TextSource source; private final Decoration decorator; // caches - private Font font; - private AffineTransform baseTX; - private CoreMetrics cm; + private final Font font; + private final AffineTransform baseTX; + private final CoreMetrics cm; + private final float advTracking; private Rectangle2D lb; private Rectangle2D ab; @@ -74,34 +75,18 @@ class ExtendedTextSourceLabel implements TextLineComponent, Decoration.Label { private StandardGlyphVector gv; private float[] charinfo; - private float advTracking; - /** * Create from a TextSource. */ public ExtendedTextSourceLabel(TextSource source, Decoration decorator) { this.source = source; this.decorator = decorator; - finishInit(); - } - - /** - * Create from a TextSource, optionally using cached data from oldLabel starting at the offset. - * If present oldLabel must have been created from a run of text that includes the text used in - * the new label. Start in source corresponds to logical character offset in oldLabel. - */ - public ExtendedTextSourceLabel(TextSource source, ExtendedTextSourceLabel oldLabel, int offset) { - // currently no optimization. - this.source = source; - this.decorator = oldLabel.decorator; - finishInit(); - } - - private void finishInit() { - font = source.getFont(); + Font font = source.getFont(); Map atts = font.getAttributes(); - baseTX = AttributeValues.getBaselineTransform(atts); + AffineTransform baseTX = AttributeValues.getBaselineTransform(atts); + + CoreMetrics cm; if (baseTX == null){ cm = source.getCoreMetrics(); } else { @@ -110,13 +95,15 @@ class ExtendedTextSourceLabel implements TextLineComponent, Decoration.Label { charTX = new AffineTransform(); } font = font.deriveFont(charTX); - LineMetrics lm = font.getLineMetrics(source.getChars(), source.getStart(), source.getStart() + source.getLength(), source.getFRC()); cm = CoreMetrics.get(lm); } - advTracking = font.getSize() * AttributeValues.getTracking(atts); + this.font = font; + this.baseTX = baseTX; + this.cm = cm; + this.advTracking = font.getSize() * AttributeValues.getTracking(atts); } /** diff --git a/src/java.desktop/share/classes/sun/font/GlyphLayout.java b/src/java.desktop/share/classes/sun/font/GlyphLayout.java index 5bff127f143..851201fe347 100644 --- a/src/java.desktop/share/classes/sun/font/GlyphLayout.java +++ b/src/java.desktop/share/classes/sun/font/GlyphLayout.java @@ -125,10 +125,10 @@ public final class GlyphLayout { } private static final class SDCache { - public AffineTransform dtx; - public AffineTransform gtx; - public Point2D.Float delta; - public FontStrikeDesc sd; + private final AffineTransform dtx; + private final AffineTransform gtx; + private final Point2D.Float delta; + private final FontStrikeDesc sd; private SDCache(Font font, FontRenderContext frc) { // !!! add getVectorTransform and hasVectorTransform to frc? then diff --git a/src/java.desktop/share/classes/sun/font/HBShaper.java b/src/java.desktop/share/classes/sun/font/HBShaper.java index 3a532072004..dea8a9e22dd 100644 --- a/src/java.desktop/share/classes/sun/font/HBShaper.java +++ b/src/java.desktop/share/classes/sun/font/HBShaper.java @@ -216,45 +216,30 @@ public class HBShaper { JAVA_INT, // return type JAVA_INT, ADDRESS); // arg types - FunctionDescriptor get_var_glyph_fd = getFunctionDescriptor(JAVA_INT, // return type - ADDRESS, ADDRESS, JAVA_INT, JAVA_INT, ADDRESS, ADDRESS); // arg types - MethodHandle get_var_glyph_mh = - getMethodHandle("get_variation_glyph", get_var_glyph_fd); - @SuppressWarnings("restricted") - MemorySegment tmp5 = LINKER.upcallStub(get_var_glyph_mh, get_var_glyph_fd, garena); - get_var_glyph_stub = tmp5; + get_var_glyph_stub = getUpcallStub(garena, + "get_variation_glyph", // method name + JAVA_INT, // return type + ADDRESS, ADDRESS, JAVA_INT, JAVA_INT, ADDRESS, ADDRESS); // arg types - FunctionDescriptor get_nominal_glyph_fd = getFunctionDescriptor(JAVA_INT, // return type - ADDRESS, ADDRESS, JAVA_INT, ADDRESS, ADDRESS); // arg types - MethodHandle get_nominal_glyph_mh = - getMethodHandle("get_nominal_glyph", get_nominal_glyph_fd); - @SuppressWarnings("restricted") - MemorySegment tmp6 = LINKER.upcallStub(get_nominal_glyph_mh, get_nominal_glyph_fd, garena); - get_nominal_glyph_stub = tmp6; + get_nominal_glyph_stub = getUpcallStub(garena, + "get_nominal_glyph", // method name + JAVA_INT, // return type + ADDRESS, ADDRESS, JAVA_INT, ADDRESS, ADDRESS); // arg types - FunctionDescriptor get_h_adv_fd = getFunctionDescriptor(JAVA_INT, // return type - ADDRESS, ADDRESS, JAVA_INT, ADDRESS); // arg types - MethodHandle get_h_adv_mh = - getMethodHandle("get_glyph_h_advance", get_h_adv_fd); - @SuppressWarnings("restricted") - MemorySegment tmp7 = LINKER.upcallStub(get_h_adv_mh, get_h_adv_fd, garena); - get_h_advance_stub = tmp7; + get_h_advance_stub = getUpcallStub(garena, + "get_glyph_h_advance", // method name + JAVA_INT, // return type + ADDRESS, ADDRESS, JAVA_INT, ADDRESS); // arg types - FunctionDescriptor get_v_adv_fd = getFunctionDescriptor(JAVA_INT, // return type - ADDRESS, ADDRESS, JAVA_INT, ADDRESS); // arg types - MethodHandle get_v_adv_mh = - getMethodHandle("get_glyph_v_advance", get_v_adv_fd); - @SuppressWarnings("restricted") - MemorySegment tmp8 = LINKER.upcallStub(get_v_adv_mh, get_v_adv_fd, garena); - get_v_advance_stub = tmp8; + get_v_advance_stub = getUpcallStub(garena, + "get_glyph_v_advance", // method name + JAVA_INT, // return type + ADDRESS, ADDRESS, JAVA_INT, ADDRESS); // arg types - FunctionDescriptor get_contour_pt_fd = getFunctionDescriptor(JAVA_INT, // return type - ADDRESS, ADDRESS, JAVA_INT, JAVA_INT, ADDRESS, ADDRESS, ADDRESS); // arg types - MethodHandle get_contour_pt_mh = - getMethodHandle("get_glyph_contour_point", get_contour_pt_fd); - @SuppressWarnings("restricted") - MemorySegment tmp9 = LINKER.upcallStub(get_contour_pt_mh, get_contour_pt_fd, garena); - get_contour_pt_stub = tmp9; + get_contour_pt_stub = getUpcallStub(garena, + "get_glyph_contour_point", // method name + JAVA_INT, // return type + ADDRESS, ADDRESS, JAVA_INT, JAVA_INT, ADDRESS, ADDRESS, ADDRESS); // arg types /* Having now created the font upcall stubs, we can call down to create * the native harfbuzz object holding these. diff --git a/src/java.desktop/share/classes/sun/font/ScriptRunData.java b/src/java.desktop/share/classes/sun/font/ScriptRunData.java index 1f1f6b44d59..4e13fb5bbd0 100644 --- a/src/java.desktop/share/classes/sun/font/ScriptRunData.java +++ b/src/java.desktop/share/classes/sun/font/ScriptRunData.java @@ -38,11 +38,12 @@ public final class ScriptRunData { private static final int CHAR_START = 0; private static final int CHAR_LIMIT = 0x110000; - private static int cache = 0; + private static volatile int cache = 0; public static int getScript(int cp) { + int lcache = cache; // optimize for runs of characters in the same script - if (cp >= data[cache] && cp < data[cache+2]) { - return data[cache+1]; + if (cp >= data[lcache] && cp < data[lcache+2]) { + return data[lcache+1]; } if ((cp >= CHAR_START) && (cp < CHAR_LIMIT)) { int probe = dataPower; diff --git a/src/java.desktop/share/classes/sun/font/SunFontManager.java b/src/java.desktop/share/classes/sun/font/SunFontManager.java index 85a948ef594..323f0d056e1 100644 --- a/src/java.desktop/share/classes/sun/font/SunFontManager.java +++ b/src/java.desktop/share/classes/sun/font/SunFontManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2026, 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 @@ -2480,30 +2480,12 @@ public abstract class SunFontManager implements FontSupport, FontManagerForSGE { * SunGraphicsEnvironment it performs the same initialization as is * performed normally. There may be some duplication of effort, but * that code is already written to be able to perform properly if called - * to duplicate work. The main difference is that if we detect we are - * in an AppContext environment these new fonts - * are not placed in the "default" maps but into an AppContext instance. - * The font lookup mechanism in java.awt.Font.getFont2D() is also updated - * so that look-up for composite fonts will in that case always - * do a lookup rather than returning a cached result. - * This is inefficient but necessary else singleton java.awt.Font - * instances would not retrieve the correct Font2D for the appcontext. - * sun.font.FontManager.findFont2D is also updated to that it uses - * a name map cache specific to that appcontext. - * - * Getting an AppContext is expensive, so there is a global variable - * that records whether these methods have ever been called and can - * avoid the expense for almost all applications. Once the correct - * CompositeFont is associated with the Font, everything should work - * through existing mechanisms. - * A special case is that GraphicsEnvironment.getAllFonts() must - * return an AppContext specific list. + * to duplicate work. * * Calling the methods below is "heavyweight" but it is expected that * these methods will be called very rarely. * - * If _usingAlternateComposites is true, we are not in an "AppContext" - * environment and the (single) application has selected + * If _usingAlternateComposites is true, the application has selected * an alternate composite font behaviour. * * - Printing: The implementation delegates logical fonts to an AWT diff --git a/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java b/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java index 891a15f24de..d66cd3fe3d5 100644 --- a/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java +++ b/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java @@ -596,7 +596,9 @@ public final class SunGraphics2D textAt.scale(ptSize, ptSize); info.originX = (float)textAt.getTranslateX(); info.originY = (float)textAt.getTranslateY(); - textAt.translate(-info.originX, -info.originY); + textAt.setTransform(textAt.getScaleX(), textAt.getShearY(), + textAt.getShearX(), textAt.getScaleY(), + 0, 0); if (transformState >= TRANSFORM_TRANSLATESCALE) { transform.getMatrix(info.devTx = new double[4]); devAt = new AffineTransform(info.devTx); diff --git a/src/java.desktop/share/classes/sun/print/RasterPrinterJob.java b/src/java.desktop/share/classes/sun/print/RasterPrinterJob.java index 32728efde6c..b28723f94a6 100644 --- a/src/java.desktop/share/classes/sun/print/RasterPrinterJob.java +++ b/src/java.desktop/share/classes/sun/print/RasterPrinterJob.java @@ -33,24 +33,23 @@ import java.awt.HeadlessException; import java.awt.KeyboardFocusManager; import java.awt.Rectangle; import java.awt.Shape; +import java.awt.Window; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.awt.print.Book; -import java.awt.print.Pageable; import java.awt.print.PageFormat; +import java.awt.print.Pageable; import java.awt.print.Paper; import java.awt.print.Printable; import java.awt.print.PrinterAbortException; import java.awt.print.PrinterException; import java.awt.print.PrinterJob; -import java.awt.Window; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Locale; -import sun.awt.image.ByteInterleavedRaster; import javax.print.Doc; import javax.print.DocFlavor; @@ -69,8 +68,8 @@ import javax.print.attribute.ResolutionSyntax; import javax.print.attribute.Size2DSyntax; import javax.print.attribute.standard.Copies; import javax.print.attribute.standard.Destination; -import javax.print.attribute.standard.DialogTypeSelection; import javax.print.attribute.standard.DialogOwner; +import javax.print.attribute.standard.DialogTypeSelection; import javax.print.attribute.standard.Fidelity; import javax.print.attribute.standard.JobName; import javax.print.attribute.standard.JobSheets; @@ -81,15 +80,17 @@ import javax.print.attribute.standard.MediaSizeName; import javax.print.attribute.standard.OrientationRequested; import javax.print.attribute.standard.OutputBin; import javax.print.attribute.standard.PageRanges; +import javax.print.attribute.standard.PrinterIsAcceptingJobs; import javax.print.attribute.standard.PrinterResolution; import javax.print.attribute.standard.PrinterState; import javax.print.attribute.standard.PrinterStateReason; import javax.print.attribute.standard.PrinterStateReasons; -import javax.print.attribute.standard.PrinterIsAcceptingJobs; import javax.print.attribute.standard.RequestingUserName; import javax.print.attribute.standard.SheetCollate; import javax.print.attribute.standard.Sides; +import sun.awt.image.ByteInterleavedRaster; + import static sun.font.FontUtilities.isIgnorableWhitespace; /** @@ -1613,8 +1614,7 @@ public abstract class RasterPrinterJob extends PrinterJob { } catch (PrinterException pe) { throw pe; } catch (Throwable printError) { - throw (PrinterException) - new PrinterException().initCause(printError.getCause()); + throw (PrinterException) new PrinterException().initCause(printError); } finally { // reset previousPaper in case this job is invoked again. previousPaper = null; diff --git a/src/java.desktop/share/legal/libpng.md b/src/java.desktop/share/legal/libpng.md index a2ffcca1974..7783fc7ff03 100644 --- a/src/java.desktop/share/legal/libpng.md +++ b/src/java.desktop/share/legal/libpng.md @@ -1,4 +1,4 @@ -## libpng v1.6.55 +## libpng v1.6.57 ### libpng License

@@ -168,6 +168,7 @@ Authors, for copyright and licensing purposes.
  * Glenn Randers-Pehrson
  * Greg Roelofs
  * Guy Eric Schalnat
+ * Halil Oktay
  * James Yu
  * John Bowler
  * Joshua Inscoe
@@ -179,6 +180,7 @@ Authors, for copyright and licensing purposes.
  * Mans Rullgard
  * Matt Sarett
  * Mike Klein
+ * Mohammad Seet
  * Pascal Massimino
  * Paul Schmidt
  * Petr Simecek
@@ -187,12 +189,14 @@ Authors, for copyright and licensing purposes.
  * Sam Bushell
  * Samuel Williams
  * Simon-Pierre Cadieux
+ * Taegu Ha (하태구)
  * Tim Wegner
  * Tobias Stoeckmann
  * Tom Lane
  * Tom Tanner
  * Vadim Barkov
  * Willem van Schaik
+ * Yuelin Wang (王跃林)
  * Zhijie Liang
  * Apple Inc.
     - Zixu Wang (王子旭)
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-common.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-common.hh
index d2ce32616be..4bb41fd0189 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-common.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-common.hh
@@ -1266,8 +1266,7 @@ struct StateTableDriver
                                   next_state == StateTableT::STATE_START_OF_TEXT &&
                                   start_state_safe_to_break_eot &&
                                   is_not_actionable &&
-                                  is_not_epsilon_transition &&
-                                  !last_range;
+                                  is_not_epsilon_transition;
 
         if (is_null_transition)
         {
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-common.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-common.hh
index dcacc9cb86c..6b62732bf54 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-common.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-common.hh
@@ -2480,7 +2480,7 @@ struct VarRegionAxis
     /* TODO Move these to sanitize(). */
     if (unlikely (start > peak || peak > end))
       return 1.f;
-    if (unlikely (start < 0 && end > 0 && peak != 0))
+    if (unlikely (start < 0 && end > 0))
       return 1.f;
 
     if (coord <= start || end <= coord)
diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/CHANGES b/src/java.desktop/share/native/libsplashscreen/libpng/CHANGES
index af9fcff6eb3..ba81df0c0e6 100644
--- a/src/java.desktop/share/native/libsplashscreen/libpng/CHANGES
+++ b/src/java.desktop/share/native/libsplashscreen/libpng/CHANGES
@@ -6337,6 +6337,48 @@ Version 1.6.55 [February 9, 2026]
   Resolved an oss-fuzz build issue involving nalloc.
     (Contributed by Philippe Antoine.)
 
+Version 1.6.56 [March 25, 2026]
+  Fixed CVE-2026-33416 (high severity):
+    Use-after-free via pointer aliasing in `png_set_tRNS` and `png_set_PLTE`.
+    (Reported by Halil Oktay and Ryo Shimada;
+    fixed by Halil Oktay and Cosmin Truta.)
+  Fixed CVE-2026-33636 (high severity):
+    Out-of-bounds read/write in the palette expansion on ARM Neon.
+    (Reported by Taegu Ha; fixed by Taegu Ha and Cosmin Truta.)
+  Fixed uninitialized reads beyond `num_trans` in `trans_alpha` buffers.
+    (Contributed by Halil Oktay.)
+  Fixed stale `info_ptr->palette` after in-place gamma and background
+    transforms.
+  Fixed wrong channel indices in `png_image_read_and_map` RGB_ALPHA path.
+    (Contributed by Yuelin Wang.)
+  Fixed wrong background color in colormap read.
+    (Contributed by Yuelin Wang.)
+  Fixed dead loop in sPLT write.
+    (Contributed by Yuelin Wang.)
+  Added missing null pointer checks in four public API functions.
+    (Contributed by Yuelin Wang.)
+  Validated shift bit depths in `png_set_shift` to prevent infinite loop.
+    (Contributed by Yuelin Wang.)
+  Avoided undefined behavior in library and tests.
+  Deprecated the hardly-ever-tested POINTER_INDEXING config option.
+  Added negative-stride test coverage for the simplified API.
+  Fixed memory leaks and API misuse in oss-fuzz.
+    (Contributed by Owen Sanzas.)
+  Implemented various fixes and improvements in oss-fuzz.
+    (Contributed by Bob Friesenhahn and Philippe Antoine.)
+  Performed various refactorings and cleanups.
+
+Version 1.6.57 [April 8, 2026]
+  Fixed CVE-2026-34757 (medium severity):
+    Use-after-free in `png_set_PLTE`, `png_set_tRNS` and `png_set_hIST`
+    leading to corrupted chunk data and potential heap information disclosure.
+    Also hardened the append-style setters (`png_set_text`, `png_set_sPLT`,
+    `png_set_unknown_chunks`) against a theoretical variant of the same
+    aliasing pattern.
+    (Reported by Iv4n .)
+  Fixed integer overflow in rowbytes computation in read transforms.
+    (Contributed by Mohammad Seet.)
+
 Send comments/corrections/commendations to png-mng-implement at lists.sf.net.
 Subscription is required; visit
 
diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/README b/src/java.desktop/share/native/libsplashscreen/libpng/README
index 6e0d1e33137..179b8dc8cb4 100644
--- a/src/java.desktop/share/native/libsplashscreen/libpng/README
+++ b/src/java.desktop/share/native/libsplashscreen/libpng/README
@@ -1,4 +1,4 @@
-README for libpng version 1.6.55
+README for libpng version 1.6.57
 ================================
 
 See the note about version numbers near the top of `png.h`.
diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/png.c b/src/java.desktop/share/native/libsplashscreen/libpng/png.c
index 955fda8dd7e..e4e13b0a684 100644
--- a/src/java.desktop/share/native/libsplashscreen/libpng/png.c
+++ b/src/java.desktop/share/native/libsplashscreen/libpng/png.c
@@ -42,7 +42,7 @@
 #include "pngpriv.h"
 
 /* Generate a compiler error if there is an old png.h in the search path. */
-typedef png_libpng_version_1_6_55 Your_png_h_is_not_version_1_6_55;
+typedef png_libpng_version_1_6_57 Your_png_h_is_not_version_1_6_57;
 
 /* Sanity check the chunks definitions - PNG_KNOWN_CHUNKS from pngpriv.h and the
  * corresponding macro definitions.  This causes a compile time failure if
@@ -849,7 +849,7 @@ png_get_copyright(png_const_structrp png_ptr)
    return PNG_STRING_COPYRIGHT
 #else
    return PNG_STRING_NEWLINE \
-      "libpng version 1.6.55" PNG_STRING_NEWLINE \
+      "libpng version 1.6.57" PNG_STRING_NEWLINE \
       "Copyright (c) 2018-2026 Cosmin Truta" PNG_STRING_NEWLINE \
       "Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson" \
       PNG_STRING_NEWLINE \
@@ -1199,7 +1199,7 @@ png_xy_from_XYZ(png_xy *xy, const png_XYZ *XYZ)
       return 1;
 
    /* The reference white is simply the sum of the end-point (X,Y,Z) vectors so
-    * the fillowing calculates (X+Y+Z) of the reference white (media white,
+    * the following calculates (X+Y+Z) of the reference white (media white,
     * encoding white) itself:
     */
    d = dblue;
@@ -1244,9 +1244,9 @@ png_XYZ_from_xy(png_XYZ *XYZ, const png_xy *xy)
     * (-0.0770) because the PNG spec itself requires the xy values to be
     * unsigned.  whitey is also required to be 5 or more to avoid overflow.
     *
-    * Instead the upper limits have been relaxed to accomodate ACES AP1 where
+    * Instead the upper limits have been relaxed to accommodate ACES AP1 where
     * redz ends up as -600 (-0.006).  ProPhotoRGB was already "in range."
-    * The new limit accomodates the AP0 and AP1 ranges for z but not AP0 redy.
+    * The new limit accommodates the AP0 and AP1 ranges for z but not AP0 redy.
     */
    const png_fixed_point fpLimit = PNG_FP_1+(PNG_FP_1/10);
    if (xy->redx   < 0 || xy->redx > fpLimit) return 1;
@@ -1357,7 +1357,7 @@ png_XYZ_from_xy(png_XYZ *XYZ, const png_xy *xy)
     *    red-scale + green-scale + blue-scale = 1/white-y = white-scale
     *
     * So now we have a Cramer's rule solution where the determinants are just
-    * 3x3 - far more tractible.  Unfortunately 3x3 determinants still involve
+    * 3x3 - far more tractable.  Unfortunately 3x3 determinants still involve
     * multiplication of three coefficients so we can't guarantee to avoid
     * overflow in the libpng fixed point representation.  Using Cramer's rule in
     * floating point is probably a good choice here, but it's not an option for
@@ -1726,7 +1726,7 @@ png_icc_check_header(png_const_structrp png_ptr, png_const_charp name,
     * into R, G and B channels.
     *
     * Previously it was suggested that an RGB profile on grayscale data could be
-    * handled.  However it it is clear that using an RGB profile in this context
+    * handled.  However it is clear that using an RGB profile in this context
     * must be an error - there is no specification of what it means.  Thus it is
     * almost certainly more correct to ignore the profile.
     */
@@ -2944,7 +2944,7 @@ png_gamma_significant(png_fixed_point gamma_val)
     *
     *    2.2/(2+51/256) == 1.00035524
     *
-    * I.e. vanishly small (<4E-4) but still detectable in 16-bit linear (+/-
+    * I.e. vanishingly small (<4E-4) but still detectable in 16-bit linear (+/-
     * 23).  Note that the Adobe choice seems to be something intended to give an
     * exact number with 8 binary fractional digits - it is the closest to 2.2
     * that is possible a base 2 .8p representation.
diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/png.h b/src/java.desktop/share/native/libsplashscreen/libpng/png.h
index e95c0444399..349e7d07383 100644
--- a/src/java.desktop/share/native/libsplashscreen/libpng/png.h
+++ b/src/java.desktop/share/native/libsplashscreen/libpng/png.h
@@ -29,7 +29,7 @@
  * However, the following notice accompanied the original version of this
  * file and, per its terms, should not be removed:
  *
- * libpng version 1.6.55
+ * libpng version 1.6.57
  *
  * Copyright (c) 2018-2026 Cosmin Truta
  * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
@@ -43,7 +43,7 @@
  *   libpng versions 0.89, June 1996, through 0.96, May 1997: Andreas Dilger
  *   libpng versions 0.97, January 1998, through 1.6.35, July 2018:
  *     Glenn Randers-Pehrson
- *   libpng versions 1.6.36, December 2018, through 1.6.55, February 2026:
+ *   libpng versions 1.6.36, December 2018, through 1.6.57, April 2026:
  *     Cosmin Truta
  *   See also "Contributing Authors", below.
  */
@@ -267,7 +267,7 @@
  *    ...
  *    1.5.30                  15    10530  15.so.15.30[.0]
  *    ...
- *    1.6.55                  16    10655  16.so.16.55[.0]
+ *    1.6.57                  16    10657  16.so.16.57[.0]
  *
  *    Henceforth the source version will match the shared-library major and
  *    minor numbers; the shared-library major version number will be used for
@@ -303,7 +303,7 @@
  */
 
 /* Version information for png.h - this should match the version in png.c */
-#define PNG_LIBPNG_VER_STRING "1.6.55"
+#define PNG_LIBPNG_VER_STRING "1.6.57"
 #define PNG_HEADER_VERSION_STRING " libpng version " PNG_LIBPNG_VER_STRING "\n"
 
 /* The versions of shared library builds should stay in sync, going forward */
@@ -314,7 +314,7 @@
 /* These should match the first 3 components of PNG_LIBPNG_VER_STRING: */
 #define PNG_LIBPNG_VER_MAJOR   1
 #define PNG_LIBPNG_VER_MINOR   6
-#define PNG_LIBPNG_VER_RELEASE 55
+#define PNG_LIBPNG_VER_RELEASE 57
 
 /* This should be zero for a public release, or non-zero for a
  * development version.
@@ -345,7 +345,7 @@
  * From version 1.0.1 it is:
  * XXYYZZ, where XX=major, YY=minor, ZZ=release
  */
-#define PNG_LIBPNG_VER 10655 /* 1.6.55 */
+#define PNG_LIBPNG_VER 10657 /* 1.6.57 */
 
 /* Library configuration: these options cannot be changed after
  * the library has been built.
@@ -455,7 +455,7 @@ extern "C" {
 /* This triggers a compiler error in png.c, if png.c and png.h
  * do not agree upon the version number.
  */
-typedef char *png_libpng_version_1_6_55;
+typedef char *png_libpng_version_1_6_57;
 
 /* Basic control structions.  Read libpng-manual.txt or libpng.3 for more info.
  *
@@ -2370,7 +2370,7 @@ PNG_EXPORT(162, int, png_get_text,
 #endif
 
 /* Note while png_set_text() will accept a structure whose text,
- * language, and  translated keywords are NULL pointers, the structure
+ * language, and translated keywords are NULL pointers, the structure
  * returned by png_get_text will always contain regular
  * zero-terminated C strings.  They might be empty strings but
  * they will never be NULL pointers.
diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngconf.h b/src/java.desktop/share/native/libsplashscreen/libpng/pngconf.h
index b957f8b5061..1a5bb7b60f8 100644
--- a/src/java.desktop/share/native/libsplashscreen/libpng/pngconf.h
+++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngconf.h
@@ -29,7 +29,7 @@
  * However, the following notice accompanied the original version of this
  * file and, per its terms, should not be removed:
  *
- * libpng version 1.6.55
+ * libpng version 1.6.57
  *
  * Copyright (c) 2018-2026 Cosmin Truta
  * Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson
diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pnglibconf.h b/src/java.desktop/share/native/libsplashscreen/libpng/pnglibconf.h
index ae1ab462072..de63c998927 100644
--- a/src/java.desktop/share/native/libsplashscreen/libpng/pnglibconf.h
+++ b/src/java.desktop/share/native/libsplashscreen/libpng/pnglibconf.h
@@ -31,7 +31,7 @@
  * However, the following notice accompanied the original version of this
  * file and, per its terms, should not be removed:
  */
-/* libpng version 1.6.55 */
+/* libpng version 1.6.57 */
 
 /* Copyright (c) 2018-2026 Cosmin Truta */
 /* Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson */
diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngpriv.h b/src/java.desktop/share/native/libsplashscreen/libpng/pngpriv.h
index ee91f58d4ba..086a2f76ee6 100644
--- a/src/java.desktop/share/native/libsplashscreen/libpng/pngpriv.h
+++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngpriv.h
@@ -986,7 +986,7 @@
  *
  * At present these index values are not exported (not part of the public API)
  * so can be changed at will.  For convenience the names are in lexical sort
- * order but with the critical chunks at the start in the order of occurence in
+ * order but with the critical chunks at the start in the order of occurrence in
  * a PNG.
  *
  * PNG_INFO_ values do not exist for every one of these chunk handles; for
@@ -2115,7 +2115,7 @@ PNG_INTERNAL_FUNCTION(void, png_ascii_from_fixed,
  * not valid it will be the index of a character in the supposed number.
  *
  * The format of a number is defined in the PNG extensions specification
- * and this API is strictly conformant to that spec, not anyone elses!
+ * and this API is strictly conformant to that spec, not anyone else's!
  *
  * The format as a regular expression is:
  *
diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngread.c b/src/java.desktop/share/native/libsplashscreen/libpng/pngread.c
index 79fd9ad6a82..70df18926f5 100644
--- a/src/java.desktop/share/native/libsplashscreen/libpng/pngread.c
+++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngread.c
@@ -720,7 +720,7 @@ png_read_end(png_structrp png_ptr, png_inforp info_ptr)
       png_read_finish_IDAT(png_ptr);
 
 #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
-   /* Report invalid palette index; added at libng-1.5.10 */
+   /* Report invalid palette index; added at libpng-1.5.10 */
    if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
        png_ptr->num_palette_max >= png_ptr->num_palette)
       png_benign_error(png_ptr, "Read palette index exceeding num_palette");
@@ -808,21 +808,19 @@ png_read_destroy(png_structrp png_ptr)
    png_ptr->quantize_index = NULL;
 #endif
 
-   if ((png_ptr->free_me & PNG_FREE_PLTE) != 0)
-   {
-      png_zfree(png_ptr, png_ptr->palette);
-      png_ptr->palette = NULL;
-   }
-   png_ptr->free_me &= ~PNG_FREE_PLTE;
+   /* png_ptr->palette is always independently allocated (not aliased
+    * with info_ptr->palette), so free it unconditionally.
+    */
+   png_free(png_ptr, png_ptr->palette);
+   png_ptr->palette = NULL;
 
 #if defined(PNG_tRNS_SUPPORTED) || \
     defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
-   if ((png_ptr->free_me & PNG_FREE_TRNS) != 0)
-   {
-      png_free(png_ptr, png_ptr->trans_alpha);
-      png_ptr->trans_alpha = NULL;
-   }
-   png_ptr->free_me &= ~PNG_FREE_TRNS;
+   /* png_ptr->trans_alpha is always independently allocated (not aliased
+    * with info_ptr->trans_alpha), so free it unconditionally.
+    */
+   png_free(png_ptr, png_ptr->trans_alpha);
+   png_ptr->trans_alpha = NULL;
 #endif
 
    inflateEnd(&png_ptr->zstream);
@@ -1285,7 +1283,7 @@ png_image_is_not_sRGB(png_const_structrp png_ptr)
     * png_struct::chromaticities always exists since the simplified API
     * requires rgb-to-gray.  The mDCV, cICP and cHRM chunks may all set it to
     * a non-sRGB value, so it needs to be checked but **only** if one of
-    * those chunks occured in the file.
+    * those chunks occurred in the file.
     */
    /* Highest priority: check to be safe. */
    if (png_has_chunk(png_ptr, cICP) || png_has_chunk(png_ptr, mDCV))
@@ -2625,7 +2623,7 @@ png_image_read_colormap(png_voidp argument)
                   {
                      r = back_r;
                      g = back_g;
-                     b = back_g;
+                     b = back_b;
                   }
 
                   /* Compare the newly-created color-map entry with the one the
@@ -2903,9 +2901,9 @@ png_image_read_and_map(png_voidp argument)
          {
             png_bytep inrow = png_voidcast(png_bytep, display->local_row);
             png_bytep outrow = first_row + y * row_step;
-            png_const_bytep end_row = outrow + width;
+            png_const_bytep row_end = outrow + width;
 
-            /* Read read the libpng data into the temporary buffer. */
+            /* Read the libpng data into the temporary buffer. */
             png_read_row(png_ptr, inrow, NULL);
 
             /* Now process the row according to the processing option, note
@@ -2916,7 +2914,7 @@ png_image_read_and_map(png_voidp argument)
             switch (proc)
             {
                case PNG_CMAP_GA:
-                  for (; outrow < end_row; outrow += stepx)
+                  for (; outrow < row_end; outrow += stepx)
                   {
                      /* The data is always in the PNG order */
                      unsigned int gray = *inrow++;
@@ -2945,7 +2943,7 @@ png_image_read_and_map(png_voidp argument)
                   break;
 
                case PNG_CMAP_TRANS:
-                  for (; outrow < end_row; outrow += stepx)
+                  for (; outrow < row_end; outrow += stepx)
                   {
                      png_byte gray = *inrow++;
                      png_byte alpha = *inrow++;
@@ -2962,7 +2960,7 @@ png_image_read_and_map(png_voidp argument)
                   break;
 
                case PNG_CMAP_RGB:
-                  for (; outrow < end_row; outrow += stepx)
+                  for (; outrow < row_end; outrow += stepx)
                   {
                      *outrow = PNG_RGB_INDEX(inrow[0], inrow[1], inrow[2]);
                      inrow += 3;
@@ -2970,7 +2968,7 @@ png_image_read_and_map(png_voidp argument)
                   break;
 
                case PNG_CMAP_RGB_ALPHA:
-                  for (; outrow < end_row; outrow += stepx)
+                  for (; outrow < row_end; outrow += stepx)
                   {
                      unsigned int alpha = inrow[3];
 
@@ -3007,10 +3005,10 @@ png_image_read_and_map(png_voidp argument)
                          */
                         if (inrow[0] & 0x80) back_i += 9; /* red */
                         if (inrow[0] & 0x40) back_i += 9;
-                        if (inrow[0] & 0x80) back_i += 3; /* green */
-                        if (inrow[0] & 0x40) back_i += 3;
-                        if (inrow[0] & 0x80) back_i += 1; /* blue */
-                        if (inrow[0] & 0x40) back_i += 1;
+                        if (inrow[1] & 0x80) back_i += 3; /* green */
+                        if (inrow[1] & 0x40) back_i += 3;
+                        if (inrow[2] & 0x80) back_i += 1; /* blue */
+                        if (inrow[2] & 0x40) back_i += 1;
 
                         *outrow = (png_byte)back_i;
                      }
@@ -3277,18 +3275,18 @@ png_image_read_composite(png_voidp argument)
          {
             png_bytep inrow = png_voidcast(png_bytep, display->local_row);
             png_bytep outrow;
-            png_const_bytep end_row;
+            png_const_bytep row_end;
 
             /* Read the row, which is packed: */
             png_read_row(png_ptr, inrow, NULL);
 
             outrow = png_voidcast(png_bytep, display->first_row);
             outrow += y * row_step;
-            end_row = outrow + width * channels;
+            row_end = outrow + width * channels;
 
             /* Now do the composition on each pixel in this row. */
             outrow += startx;
-            for (; outrow < end_row; outrow += stepx)
+            for (; outrow < row_end; outrow += stepx)
             {
                png_byte alpha = inrow[channels];
 
@@ -3461,14 +3459,14 @@ png_image_read_background(png_voidp argument)
                      png_bytep inrow = png_voidcast(png_bytep,
                          display->local_row);
                      png_bytep outrow = first_row + y * row_step;
-                     png_const_bytep end_row = outrow + width;
+                     png_const_bytep row_end = outrow + width;
 
                      /* Read the row, which is packed: */
                      png_read_row(png_ptr, inrow, NULL);
 
                      /* Now do the composition on each pixel in this row. */
                      outrow += startx;
-                     for (; outrow < end_row; outrow += stepx)
+                     for (; outrow < row_end; outrow += stepx)
                      {
                         png_byte alpha = inrow[1];
 
@@ -3506,14 +3504,14 @@ png_image_read_background(png_voidp argument)
                      png_bytep inrow = png_voidcast(png_bytep,
                          display->local_row);
                      png_bytep outrow = first_row + y * row_step;
-                     png_const_bytep end_row = outrow + width;
+                     png_const_bytep row_end = outrow + width;
 
                      /* Read the row, which is packed: */
                      png_read_row(png_ptr, inrow, NULL);
 
                      /* Now do the composition on each pixel in this row. */
                      outrow += startx;
-                     for (; outrow < end_row; outrow += stepx)
+                     for (; outrow < row_end; outrow += stepx)
                      {
                         png_byte alpha = inrow[1];
 
@@ -3596,7 +3594,7 @@ png_image_read_background(png_voidp argument)
                {
                   png_const_uint_16p inrow;
                   png_uint_16p outrow = first_row + y * row_step;
-                  png_uint_16p end_row = outrow + width * outchannels;
+                  png_uint_16p row_end = outrow + width * outchannels;
 
                   /* Read the row, which is packed: */
                   png_read_row(png_ptr, png_voidcast(png_bytep,
@@ -3606,7 +3604,7 @@ png_image_read_background(png_voidp argument)
                   /* Now do the pre-multiplication on each pixel in this row.
                    */
                   outrow += startx;
-                  for (; outrow < end_row; outrow += stepx)
+                  for (; outrow < row_end; outrow += stepx)
                   {
                      png_uint_32 component = inrow[0];
                      png_uint_16 alpha = inrow[1];
@@ -4142,7 +4140,7 @@ png_image_finish_read(png_imagep image, png_const_colorp background,
             row_stride = (png_int_32)/*SAFE*/png_row_stride;
 
          if (row_stride < 0)
-            check = (png_uint_32)(-row_stride);
+            check = -(png_uint_32)row_stride;
 
          else
             check = (png_uint_32)row_stride;
diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngrtran.c b/src/java.desktop/share/native/libsplashscreen/libpng/pngrtran.c
index fcce80da1cb..838c8460f91 100644
--- a/src/java.desktop/share/native/libsplashscreen/libpng/pngrtran.c
+++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngrtran.c
@@ -259,7 +259,7 @@ png_set_strip_alpha(png_structrp png_ptr)
  *
  * Terminology (assuming power law, "gamma", encodings):
  *    "screen" gamma: a power law imposed by the output device when digital
- *    samples are converted to visible light output.  The EOTF - volage to
+ *    samples are converted to visible light output.  The EOTF - voltage to
  *    luminance on output.
  *
  *    "file" gamma: a power law used to encode luminance levels from the input
@@ -524,6 +524,9 @@ png_set_quantize(png_structrp png_ptr, png_colorp palette,
    if (png_rtran_ok(png_ptr, 0) == 0)
       return;
 
+   if (palette == NULL)
+      return;
+
    png_ptr->transformations |= PNG_QUANTIZE;
 
    if (full_quantize == 0)
@@ -840,7 +843,13 @@ png_set_quantize(png_structrp png_ptr, png_colorp palette,
    }
    if (png_ptr->palette == NULL)
    {
-      png_ptr->palette = palette;
+      /* Allocate an owned copy rather than aliasing the caller's pointer,
+       * so that png_read_destroy can free png_ptr->palette unconditionally.
+       */
+      png_ptr->palette = png_voidcast(png_colorp, png_calloc(png_ptr,
+          PNG_MAX_PALETTE_LENGTH * (sizeof (png_color))));
+      memcpy(png_ptr->palette, palette, (unsigned int)num_palette *
+          (sizeof (png_color)));
    }
    png_ptr->num_palette = (png_uint_16)num_palette;
 
@@ -1393,7 +1402,7 @@ png_resolve_file_gamma(png_const_structrp png_ptr)
    if (file_gamma != 0)
       return file_gamma;
 
-   /* If png_reciprocal oveflows it returns 0 which indicates to the caller that
+   /* If png_reciprocal overflows, it returns 0, indicating to the caller that
     * there is no usable file gamma.  (The checks added to png_set_gamma and
     * png_set_alpha_mode should prevent a screen_gamma which would overflow.)
     */
@@ -2090,6 +2099,21 @@ png_read_transform_info(png_structrp png_ptr, png_inforp info_ptr)
 {
    png_debug(1, "in png_read_transform_info");
 
+   if (png_ptr->transformations != 0)
+   {
+      if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
+          info_ptr->palette != NULL && png_ptr->palette != NULL)
+      {
+         /* Sync info_ptr->palette with png_ptr->palette.
+          * The function png_init_read_transformations may have modified
+          * png_ptr->palette in place (e.g. for gamma correction or for
+          * background compositing).
+          */
+         memcpy(info_ptr->palette, png_ptr->palette,
+             PNG_MAX_PALETTE_LENGTH * (sizeof (png_color)));
+      }
+   }
+
 #ifdef PNG_READ_EXPAND_SUPPORTED
    if ((png_ptr->transformations & PNG_EXPAND) != 0)
    {
@@ -2384,7 +2408,7 @@ png_do_unpack(png_row_infop row_info, png_bytep row)
       }
       row_info->bit_depth = 8;
       row_info->pixel_depth = (png_byte)(8 * row_info->channels);
-      row_info->rowbytes = row_width * row_info->channels;
+      row_info->rowbytes = (size_t)row_width * row_info->channels;
    }
 }
 #endif
@@ -2586,7 +2610,7 @@ png_do_scale_16_to_8(png_row_infop row_info, png_bytep row)
 
       row_info->bit_depth = 8;
       row_info->pixel_depth = (png_byte)(8 * row_info->channels);
-      row_info->rowbytes = row_info->width * row_info->channels;
+      row_info->rowbytes = (size_t)row_info->width * row_info->channels;
    }
 }
 #endif
@@ -2614,7 +2638,7 @@ png_do_chop(png_row_infop row_info, png_bytep row)
 
       row_info->bit_depth = 8;
       row_info->pixel_depth = (png_byte)(8 * row_info->channels);
-      row_info->rowbytes = row_info->width * row_info->channels;
+      row_info->rowbytes = (size_t)row_info->width * row_info->channels;
    }
 }
 #endif
@@ -2850,7 +2874,7 @@ png_do_read_filler(png_row_infop row_info, png_bytep row,
             *(--dp) = lo_filler;
             row_info->channels = 2;
             row_info->pixel_depth = 16;
-            row_info->rowbytes = row_width * 2;
+            row_info->rowbytes = (size_t)row_width * 2;
          }
 
          else
@@ -2865,7 +2889,7 @@ png_do_read_filler(png_row_infop row_info, png_bytep row,
             }
             row_info->channels = 2;
             row_info->pixel_depth = 16;
-            row_info->rowbytes = row_width * 2;
+            row_info->rowbytes = (size_t)row_width * 2;
          }
       }
 
@@ -2888,7 +2912,7 @@ png_do_read_filler(png_row_infop row_info, png_bytep row,
             *(--dp) = hi_filler;
             row_info->channels = 2;
             row_info->pixel_depth = 32;
-            row_info->rowbytes = row_width * 4;
+            row_info->rowbytes = (size_t)row_width * 4;
          }
 
          else
@@ -2905,7 +2929,7 @@ png_do_read_filler(png_row_infop row_info, png_bytep row,
             }
             row_info->channels = 2;
             row_info->pixel_depth = 32;
-            row_info->rowbytes = row_width * 4;
+            row_info->rowbytes = (size_t)row_width * 4;
          }
       }
 #endif
@@ -2929,7 +2953,7 @@ png_do_read_filler(png_row_infop row_info, png_bytep row,
             *(--dp) = lo_filler;
             row_info->channels = 4;
             row_info->pixel_depth = 32;
-            row_info->rowbytes = row_width * 4;
+            row_info->rowbytes = (size_t)row_width * 4;
          }
 
          else
@@ -2946,7 +2970,7 @@ png_do_read_filler(png_row_infop row_info, png_bytep row,
             }
             row_info->channels = 4;
             row_info->pixel_depth = 32;
-            row_info->rowbytes = row_width * 4;
+            row_info->rowbytes = (size_t)row_width * 4;
          }
       }
 
@@ -2973,7 +2997,7 @@ png_do_read_filler(png_row_infop row_info, png_bytep row,
             *(--dp) = hi_filler;
             row_info->channels = 4;
             row_info->pixel_depth = 64;
-            row_info->rowbytes = row_width * 8;
+            row_info->rowbytes = (size_t)row_width * 8;
          }
 
          else
@@ -2995,7 +3019,7 @@ png_do_read_filler(png_row_infop row_info, png_bytep row,
 
             row_info->channels = 4;
             row_info->pixel_depth = 64;
-            row_info->rowbytes = row_width * 8;
+            row_info->rowbytes = (size_t)row_width * 8;
          }
       }
 #endif
@@ -4489,7 +4513,7 @@ png_do_expand_palette(png_structrp png_ptr, png_row_infop row_info,
                }
                row_info->bit_depth = 8;
                row_info->pixel_depth = 32;
-               row_info->rowbytes = row_width * 4;
+               row_info->rowbytes = (size_t)row_width * 4;
                row_info->color_type = 6;
                row_info->channels = 4;
             }
@@ -4497,7 +4521,7 @@ png_do_expand_palette(png_structrp png_ptr, png_row_infop row_info,
             else
             {
                sp = row + (size_t)row_width - 1;
-               dp = row + (size_t)(row_width * 3) - 1;
+               dp = row + (size_t)row_width * 3 - 1;
                i = 0;
 #ifdef PNG_ARM_NEON_INTRINSICS_AVAILABLE
                i = png_do_expand_palette_rgb8_neon(png_ptr, row_info, row,
@@ -4516,7 +4540,7 @@ png_do_expand_palette(png_structrp png_ptr, png_row_infop row_info,
 
                row_info->bit_depth = 8;
                row_info->pixel_depth = 24;
-               row_info->rowbytes = row_width * 3;
+               row_info->rowbytes = (size_t)row_width * 3;
                row_info->color_type = 2;
                row_info->channels = 3;
             }
diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngrutil.c b/src/java.desktop/share/native/libsplashscreen/libpng/pngrutil.c
index 01bb0c8bedc..4712dfd418a 100644
--- a/src/java.desktop/share/native/libsplashscreen/libpng/pngrutil.c
+++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngrutil.c
@@ -465,7 +465,7 @@ png_inflate_claim(png_structrp png_ptr, png_uint_32 owner)
     * be gained by using this when it is known *if* the zlib stream itself does
     * not record the number; however, this is an illusion: the original writer
     * of the PNG may have selected a lower window size, and we really must
-    * follow that because, for systems with with limited capabilities, we
+    * follow that because, for systems with limited capabilities, we
     * would otherwise reject the application's attempts to use a smaller window
     * size (zlib doesn't have an interface to say "this or lower"!).
     *
@@ -1035,7 +1035,7 @@ png_handle_PLTE(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
     * in the case of an 8-bit display with a decoder which controls the palette.
     *
     * The alternative here is to ignore the error and store the palette anyway;
-    * destroying the tRNS will definately cause problems.
+    * destroying the tRNS will definitely cause problems.
     *
     * NOTE: the case of PNG_COLOR_TYPE_PALETTE need not be considered because
     * the png_handle_ routines for the three 'after PLTE' chunks tRNS, bKGD and
@@ -1082,19 +1082,6 @@ png_handle_PLTE(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
       /* A valid PLTE chunk has been read */
       png_ptr->mode |= PNG_HAVE_PLTE;
 
-      /* TODO: png_set_PLTE has the side effect of setting png_ptr->palette to
-       * its own copy of the palette.  This has the side effect that when
-       * png_start_row is called (this happens after any call to
-       * png_read_update_info) the info_ptr palette gets changed.  This is
-       * extremely unexpected and confusing.
-       *
-       * REVIEW: there have been consistent bugs in the past about gamma and
-       * similar transforms to colour mapped images being useless because the
-       * modified palette cannot be accessed because of the above.
-       *
-       * CONSIDER: Fix this by not sharing the palette in this way.  But does
-       * this completely fix the problem?
-       */
       png_set_PLTE(png_ptr, info_ptr, palette, num);
       return handled_ok;
    }
@@ -1296,7 +1283,7 @@ png_handle_cHRM(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 
    /* png_set_cHRM may complain about some of the values but this doesn't matter
     * because it was a cHRM and it did have vaguely (if, perhaps, ridiculous)
-    * values.  Ridiculousity will be checked if the values are used later.
+    * values.  Ridiculosity will be checked if the values are used later.
     */
    png_set_cHRM_fixed(png_ptr, info_ptr, xy.whitex, xy.whitey, xy.redx, xy.redy,
          xy.greenx, xy.greeny, xy.bluex, xy.bluey);
@@ -1593,7 +1580,8 @@ static png_handle_result_code /* PRIVATE */
 png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 /* Note: this does not properly handle chunks that are > 64K under DOS */
 {
-   png_bytep entry_start, buffer;
+   png_bytep buffer;
+   png_bytep entry_start;
    png_sPLT_t new_palette;
    png_sPLT_entryp pp;
    png_uint_32 data_length;
@@ -1800,10 +1788,6 @@ png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
       return handled_error;
    }
 
-   /* TODO: this is a horrible side effect in the palette case because the
-    * png_struct ends up with a pointer to the tRNS buffer owned by the
-    * png_info.  Fix this.
-    */
    png_set_tRNS(png_ptr, info_ptr, readbuf, png_ptr->num_trans,
        &(png_ptr->trans_color));
    return handled_ok;
@@ -2062,7 +2046,7 @@ png_handle_eXIf(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
       return handled_error;
 
    /* PNGv3: the code used to check the byte order mark at the start for MM or
-    * II, however PNGv3 states that the the first 4 bytes should be checked.
+    * II, however PNGv3 states that the first 4 bytes should be checked.
     * The caller ensures that there are four bytes available.
     */
    {
@@ -2184,9 +2168,13 @@ png_handle_oFFs(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 static png_handle_result_code /* PRIVATE */
 png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
+   png_bytep buffer;
+   png_bytep buf;
+   png_bytep endptr;
    png_int_32 X0, X1;
-   png_byte type, nparams;
-   png_bytep buffer, buf, units, endptr;
+   png_byte type;
+   png_byte nparams;
+   png_byte *units;
    png_charpp params;
    int i;
 
@@ -3040,7 +3028,7 @@ static const struct
    png_uint_32 max_length :12; /* Length min, max in bytes */
    png_uint_32 min_length :8;
       /* Length errors on critical chunks have special handling to preserve the
-       * existing behaviour in libpng 1.6.  Anciallary chunks are checked below
+       * existing behaviour in libpng 1.6.  Ancillary chunks are checked below
        * and produce a 'benign' error.
        */
    png_uint_32 pos_before :4; /* PNG_HAVE_ values chunk must precede */
@@ -3048,7 +3036,7 @@ static const struct
       /* NOTE: PLTE, tRNS and bKGD require special handling which depends on
        * the colour type of the base image.
        */
-   png_uint_32 multiple   :1; /* Multiple occurences permitted */
+   png_uint_32 multiple   :1; /* Multiple occurrences permitted */
       /* This is enabled for PLTE because PLTE may, in practice, be optional */
 }
 read_chunks[PNG_INDEX_unknown] =
@@ -3082,7 +3070,7 @@ read_chunks[PNG_INDEX_unknown] =
 #  define CDIHDR      13U,   13U,  hIHDR,     0,        0
 #  define CDPLTE  NoCheck,    0U,      0, hIHDR,        1
       /* PLTE errors are only critical for colour-map images, consequently the
-       * hander does all the checks.
+       * handler does all the checks.
        */
 #  define CDIDAT  NoCheck,    0U,  aIDAT, hIHDR,        1
 #  define CDIEND  NoCheck,    0U,      0, aIDAT,        0
diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngset.c b/src/java.desktop/share/native/libsplashscreen/libpng/pngset.c
index 0b2844f1864..29082a6be08 100644
--- a/src/java.desktop/share/native/libsplashscreen/libpng/pngset.c
+++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngset.c
@@ -29,7 +29,7 @@
  * However, the following notice accompanied the original version of this
  * file and, per its terms, should not be removed:
  *
- * Copyright (c) 2018-2025 Cosmin Truta
+ * Copyright (c) 2018-2026 Cosmin Truta
  * Copyright (c) 1998-2018 Glenn Randers-Pehrson
  * Copyright (c) 1996-1997 Andreas Dilger
  * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
@@ -362,7 +362,8 @@ png_set_eXIf_1(png_const_structrp png_ptr, png_inforp info_ptr,
    png_debug1(1, "in %s storage function", "eXIf");
 
    if (png_ptr == NULL || info_ptr == NULL ||
-       (png_ptr->mode & PNG_WROTE_eXIf) != 0)
+       (png_ptr->mode & PNG_WROTE_eXIf) != 0 ||
+       exif == NULL)
       return;
 
    new_exif = png_voidcast(png_bytep, png_malloc_warn(png_ptr, num_exif));
@@ -413,11 +414,12 @@ void PNGAPI
 png_set_hIST(png_const_structrp png_ptr, png_inforp info_ptr,
     png_const_uint_16p hist)
 {
+   png_uint_16 safe_hist[PNG_MAX_PALETTE_LENGTH];
    int i;
 
    png_debug1(1, "in %s storage function", "hIST");
 
-   if (png_ptr == NULL || info_ptr == NULL)
+   if (png_ptr == NULL || info_ptr == NULL || hist == NULL)
       return;
 
    if (info_ptr->num_palette == 0 || info_ptr->num_palette
@@ -429,6 +431,13 @@ png_set_hIST(png_const_structrp png_ptr, png_inforp info_ptr,
       return;
    }
 
+   /* Snapshot the caller's hist before freeing, in case it points to
+    * info_ptr->hist (getter-to-setter aliasing).
+    */
+   memcpy(safe_hist, hist, (unsigned int)info_ptr->num_palette *
+       (sizeof (png_uint_16)));
+   hist = safe_hist;
+
    png_free_data(png_ptr, info_ptr, PNG_FREE_HIST, 0);
 
    /* Changed from info->num_palette to PNG_MAX_PALETTE_LENGTH in
@@ -770,7 +779,7 @@ void PNGAPI
 png_set_PLTE(png_structrp png_ptr, png_inforp info_ptr,
     png_const_colorp palette, int num_palette)
 {
-
+   png_color safe_palette[PNG_MAX_PALETTE_LENGTH];
    png_uint_32 max_palette_length;
 
    png_debug1(1, "in %s storage function", "PLTE");
@@ -804,28 +813,47 @@ png_set_PLTE(png_structrp png_ptr, png_inforp info_ptr,
       png_error(png_ptr, "Invalid palette");
    }
 
-   /* It may not actually be necessary to set png_ptr->palette here;
-    * we do it for backward compatibility with the way the png_handle_tRNS
-    * function used to do the allocation.
-    *
-    * 1.6.0: the above statement appears to be incorrect; something has to set
-    * the palette inside png_struct on read.
+   /* Snapshot the caller's palette before freeing, in case it points to
+    * info_ptr->palette (getter-to-setter aliasing).
     */
+   if (num_palette > 0)
+      memcpy(safe_palette, palette, (unsigned int)num_palette *
+          (sizeof (png_color)));
+
+   palette = safe_palette;
+
    png_free_data(png_ptr, info_ptr, PNG_FREE_PLTE, 0);
 
    /* Changed in libpng-1.2.1 to allocate PNG_MAX_PALETTE_LENGTH instead
     * of num_palette entries, in case of an invalid PNG file or incorrect
     * call to png_set_PLTE() with too-large sample values.
+    *
+    * Allocate independent buffers for info_ptr and png_ptr so that the
+    * lifetime of png_ptr->palette is decoupled from the lifetime of
+    * info_ptr->palette.  Previously, these two pointers were aliased,
+    * which caused a use-after-free vulnerability if png_free_data freed
+    * info_ptr->palette while png_ptr->palette was still in use by the
+    * row transform functions (e.g. png_do_expand_palette).
+    *
+    * Both buffers are allocated with png_calloc to zero-fill, because
+    * the ARM NEON palette riffle reads all 256 entries unconditionally,
+    * regardless of num_palette.
     */
+   png_free(png_ptr, png_ptr->palette);
    png_ptr->palette = png_voidcast(png_colorp, png_calloc(png_ptr,
        PNG_MAX_PALETTE_LENGTH * (sizeof (png_color))));
+   info_ptr->palette = png_voidcast(png_colorp, png_calloc(png_ptr,
+       PNG_MAX_PALETTE_LENGTH * (sizeof (png_color))));
+   png_ptr->num_palette = info_ptr->num_palette = (png_uint_16)num_palette;
 
    if (num_palette > 0)
+   {
+      memcpy(info_ptr->palette, palette, (unsigned int)num_palette *
+          (sizeof (png_color)));
       memcpy(png_ptr->palette, palette, (unsigned int)num_palette *
           (sizeof (png_color)));
+   }
 
-   info_ptr->palette = png_ptr->palette;
-   info_ptr->num_palette = png_ptr->num_palette = (png_uint_16)num_palette;
    info_ptr->free_me |= PNG_FREE_PLTE;
    info_ptr->valid |= PNG_INFO_PLTE;
 }
@@ -955,6 +983,7 @@ png_set_text_2(png_const_structrp png_ptr, png_inforp info_ptr,
     png_const_textp text_ptr, int num_text)
 {
    int i;
+   png_textp old_text = NULL;
 
    png_debug1(1, "in text storage function, chunk typeid = 0x%lx",
       png_ptr == NULL ? 0xabadca11UL : (unsigned long)png_ptr->chunk_name);
@@ -1002,7 +1031,10 @@ png_set_text_2(png_const_structrp png_ptr, png_inforp info_ptr,
          return 1;
       }
 
-      png_free(png_ptr, info_ptr->text);
+      /* Defer freeing the old array until after the copy loop below,
+       * in case text_ptr aliases info_ptr->text (getter-to-setter).
+       */
+      old_text = info_ptr->text;
 
       info_ptr->text = new_text;
       info_ptr->free_me |= PNG_FREE_TEXT;
@@ -1087,6 +1119,7 @@ png_set_text_2(png_const_structrp png_ptr, png_inforp info_ptr,
       {
          png_chunk_report(png_ptr, "text chunk: out of memory",
              PNG_CHUNK_WRITE_ERROR);
+         png_free(png_ptr, old_text);
 
          return 1;
       }
@@ -1140,6 +1173,8 @@ png_set_text_2(png_const_structrp png_ptr, png_inforp info_ptr,
       png_debug1(3, "transferred text chunk %d", info_ptr->num_text);
    }
 
+   png_free(png_ptr, old_text);
+
    return 0;
 }
 #endif
@@ -1183,28 +1218,50 @@ png_set_tRNS(png_structrp png_ptr, png_inforp info_ptr,
 
    if (trans_alpha != NULL)
    {
-       /* It may not actually be necessary to set png_ptr->trans_alpha here;
-        * we do it for backward compatibility with the way the png_handle_tRNS
-        * function used to do the allocation.
-        *
-        * 1.6.0: The above statement is incorrect; png_handle_tRNS effectively
-        * relies on png_set_tRNS storing the information in png_struct
-        * (otherwise it won't be there for the code in pngrtran.c).
+       /* Snapshot the caller's trans_alpha before freeing, in case it
+        * points to info_ptr->trans_alpha (getter-to-setter aliasing).
         */
+       png_byte safe_trans[PNG_MAX_PALETTE_LENGTH];
+
+       if (num_trans > 0 && num_trans <= PNG_MAX_PALETTE_LENGTH)
+          memcpy(safe_trans, trans_alpha, (size_t)num_trans);
+
+       trans_alpha = safe_trans;
 
        png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, 0);
 
        if (num_trans > 0 && num_trans <= PNG_MAX_PALETTE_LENGTH)
        {
-         /* Changed from num_trans to PNG_MAX_PALETTE_LENGTH in version 1.2.1 */
+          /* Allocate info_ptr's copy of the transparency data.
+           * Initialize all entries to fully opaque (0xff), then overwrite
+           * the first num_trans entries with the actual values.
+           */
           info_ptr->trans_alpha = png_voidcast(png_bytep,
               png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH));
+          memset(info_ptr->trans_alpha, 0xff, PNG_MAX_PALETTE_LENGTH);
           memcpy(info_ptr->trans_alpha, trans_alpha, (size_t)num_trans);
-
           info_ptr->free_me |= PNG_FREE_TRNS;
           info_ptr->valid |= PNG_INFO_tRNS;
+
+          /* Allocate an independent copy for png_struct, so that the
+           * lifetime of png_ptr->trans_alpha is decoupled from the
+           * lifetime of info_ptr->trans_alpha.  Previously these two
+           * pointers were aliased, which caused a use-after-free if
+           * png_free_data freed info_ptr->trans_alpha while
+           * png_ptr->trans_alpha was still in use by the row transform
+           * functions (e.g. png_do_expand_palette).
+           */
+          png_free(png_ptr, png_ptr->trans_alpha);
+          png_ptr->trans_alpha = png_voidcast(png_bytep,
+              png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH));
+          memset(png_ptr->trans_alpha, 0xff, PNG_MAX_PALETTE_LENGTH);
+          memcpy(png_ptr->trans_alpha, trans_alpha, (size_t)num_trans);
+       }
+       else
+       {
+          png_free(png_ptr, png_ptr->trans_alpha);
+          png_ptr->trans_alpha = NULL;
        }
-       png_ptr->trans_alpha = info_ptr->trans_alpha;
    }
 
    if (trans_color != NULL)
@@ -1255,6 +1312,7 @@ png_set_sPLT(png_const_structrp png_ptr,
  */
 {
    png_sPLT_tp np;
+   png_sPLT_tp old_spalettes;
 
    png_debug1(1, "in %s storage function", "sPLT");
 
@@ -1275,7 +1333,10 @@ png_set_sPLT(png_const_structrp png_ptr,
       return;
    }
 
-   png_free(png_ptr, info_ptr->splt_palettes);
+   /* Defer freeing the old array until after the copy loop below,
+    * in case entries aliases info_ptr->splt_palettes (getter-to-setter).
+    */
+   old_spalettes = info_ptr->splt_palettes;
 
    info_ptr->splt_palettes = np;
    info_ptr->free_me |= PNG_FREE_SPLT;
@@ -1339,6 +1400,8 @@ png_set_sPLT(png_const_structrp png_ptr,
    }
    while (--nentries);
 
+   png_free(png_ptr, old_spalettes);
+
    if (nentries > 0)
       png_chunk_report(png_ptr, "sPLT out of memory", PNG_CHUNK_WRITE_ERROR);
 }
@@ -1387,6 +1450,7 @@ png_set_unknown_chunks(png_const_structrp png_ptr,
     png_inforp info_ptr, png_const_unknown_chunkp unknowns, int num_unknowns)
 {
    png_unknown_chunkp np;
+   png_unknown_chunkp old_unknowns;
 
    if (png_ptr == NULL || info_ptr == NULL || num_unknowns <= 0 ||
        unknowns == NULL)
@@ -1433,7 +1497,10 @@ png_set_unknown_chunks(png_const_structrp png_ptr,
       return;
    }
 
-   png_free(png_ptr, info_ptr->unknown_chunks);
+   /* Defer freeing the old array until after the copy loop below,
+    * in case unknowns aliases info_ptr->unknown_chunks (getter-to-setter).
+    */
+   old_unknowns = info_ptr->unknown_chunks;
 
    info_ptr->unknown_chunks = np; /* safe because it is initialized */
    info_ptr->free_me |= PNG_FREE_UNKN;
@@ -1479,6 +1546,8 @@ png_set_unknown_chunks(png_const_structrp png_ptr,
       ++np;
       ++(info_ptr->unknown_chunks_num);
    }
+
+   png_free(png_ptr, old_unknowns);
 }
 
 void PNGAPI
@@ -1902,7 +1971,7 @@ png_set_benign_errors(png_structrp png_ptr, int allowed)
 #endif /* BENIGN_ERRORS */
 
 #ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED
-   /* Whether to report invalid palette index; added at libng-1.5.10.
+   /* Whether to report invalid palette index; added at libpng-1.5.10.
     * It is possible for an indexed (color-type==3) PNG file to contain
     * pixels with invalid (out-of-range) indexes if the PLTE chunk has
     * fewer entries than the image's bit-depth would allow. We recover
diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngstruct.h b/src/java.desktop/share/native/libsplashscreen/libpng/pngstruct.h
index 8edb4bc393a..f02365e8d8e 100644
--- a/src/java.desktop/share/native/libsplashscreen/libpng/pngstruct.h
+++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngstruct.h
@@ -29,7 +29,7 @@
  * However, the following notice accompanied the original version of this
  * file and, per its terms, should not be removed:
  *
- * Copyright (c) 2018-2025 Cosmin Truta
+ * Copyright (c) 2018-2026 Cosmin Truta
  * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
  * Copyright (c) 1996-1997 Andreas Dilger
  * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
@@ -135,7 +135,7 @@ typedef enum
  * TODO: C23: convert these macros to C23 inlines (which are static).
  */
 #define png_chunk_flag_from_index(i) (0x80000000U >> (31 - (i)))
-   /* The flag coresponding to the given png_index enum value.  This is defined
+   /* The flag corresponding to the given png_index enum value.  This is defined
     * for png_unknown as well (until it reaches the value 32) but this should
     * not be relied on.
     */
@@ -144,7 +144,7 @@ typedef enum
    (((png_ptr)->chunks & png_chunk_flag_from_index(i)) != 0)
    /* The chunk has been recorded in png_struct */
 
-#define png_file_add_chunk(pnt_ptr, i)\
+#define png_file_add_chunk(png_ptr, i)\
    ((void)((png_ptr)->chunks |= png_chunk_flag_from_index(i)))
    /* Record the chunk in the png_struct */
 
diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngtrans.c b/src/java.desktop/share/native/libsplashscreen/libpng/pngtrans.c
index b9f6cb5d437..86ff2812e23 100644
--- a/src/java.desktop/share/native/libsplashscreen/libpng/pngtrans.c
+++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngtrans.c
@@ -29,7 +29,7 @@
  * However, the following notice accompanied the original version of this
  * file and, per its terms, should not be removed:
  *
- * Copyright (c) 2018-2024 Cosmin Truta
+ * Copyright (c) 2018-2026 Cosmin Truta
  * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
  * Copyright (c) 1996-1997 Andreas Dilger
  * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
@@ -113,9 +113,38 @@ png_set_shift(png_structrp png_ptr, png_const_color_8p true_bits)
 {
    png_debug(1, "in png_set_shift");
 
-   if (png_ptr == NULL)
+   if (png_ptr == NULL || true_bits == NULL)
       return;
 
+   /* Check the shift values before passing them on to png_do_shift. */
+   {
+      png_byte bit_depth = png_ptr->bit_depth;
+      int invalid = 0;
+
+      if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0)
+      {
+         if (true_bits->red == 0 || true_bits->red > bit_depth ||
+             true_bits->green == 0 || true_bits->green > bit_depth ||
+             true_bits->blue == 0 || true_bits->blue > bit_depth)
+            invalid = 1;
+      }
+      else
+      {
+         if (true_bits->gray == 0 || true_bits->gray > bit_depth)
+            invalid = 1;
+      }
+
+      if ((png_ptr->color_type & PNG_COLOR_MASK_ALPHA) != 0 &&
+          (true_bits->alpha == 0 || true_bits->alpha > bit_depth))
+         invalid = 1;
+
+      if (invalid)
+      {
+         png_app_error(png_ptr, "png_set_shift: invalid shift values");
+         return;
+      }
+   }
+
    png_ptr->transformations |= PNG_SHIFT;
    png_ptr->shift = *true_bits;
 }
@@ -486,10 +515,9 @@ png_do_packswap(png_row_infop row_info, png_bytep row)
 
    if (row_info->bit_depth < 8)
    {
+      png_const_bytep table;
       png_bytep rp;
-      png_const_bytep end, table;
-
-      end = row + row_info->rowbytes;
+      png_bytep row_end = row + row_info->rowbytes;
 
       if (row_info->bit_depth == 1)
          table = onebppswaptable;
@@ -503,7 +531,7 @@ png_do_packswap(png_row_infop row_info, png_bytep row)
       else
          return;
 
-      for (rp = row; rp < end; rp++)
+      for (rp = row; rp < row_end; rp++)
          *rp = table[*rp];
    }
 }
diff --git a/src/java.desktop/unix/classes/sun/awt/X11/InfoWindow.java b/src/java.desktop/unix/classes/sun/awt/X11/InfoWindow.java
index bab0f34f90f..c03eae62225 100644
--- a/src/java.desktop/unix/classes/sun/awt/X11/InfoWindow.java
+++ b/src/java.desktop/unix/classes/sun/awt/X11/InfoWindow.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2026, 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
@@ -31,6 +31,7 @@ import java.awt.Color;
 import java.awt.Component;
 import java.awt.Container;
 import java.awt.Dimension;
+import java.awt.EventQueue;
 import java.awt.Font;
 import java.awt.Frame;
 import java.awt.GridLayout;
@@ -91,7 +92,7 @@ public abstract class InfoWindow extends Window {
     // Must be executed on EDT.
     @SuppressWarnings("deprecation")
     protected void show(Point corner, int indent) {
-        assert SunToolkit.isDispatchThreadForAppContext(this);
+        assert EventQueue.isDispatchThread();
 
         pack();
 
@@ -464,7 +465,7 @@ public abstract class InfoWindow extends Window {
                     ActionEvent aev = new ActionEvent(target, ActionEvent.ACTION_PERFORMED,
                                                       liveArguments.getActionCommand(),
                                                       e.getWhen(), e.getModifiers());
-                    XToolkit.postEvent(XToolkit.targetToAppContext(aev.getSource()), aev);
+                    XToolkit.postEvent(aev);
                 }
             }
         }
diff --git a/src/java.desktop/unix/classes/sun/awt/X11/XBaseMenuWindow.java b/src/java.desktop/unix/classes/sun/awt/X11/XBaseMenuWindow.java
index f8fcb30d8d8..9512d1d0351 100644
--- a/src/java.desktop/unix/classes/sun/awt/X11/XBaseMenuWindow.java
+++ b/src/java.desktop/unix/classes/sun/awt/X11/XBaseMenuWindow.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2026, 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
@@ -114,8 +114,6 @@ public abstract class XBaseMenuWindow extends XWindow {
     protected Point grabInputPoint = null;
     protected boolean hasPointerMoved = false;
 
-    private AppContext disposeAppContext;
-
     /************************************************
      *
      * Mapping data
@@ -175,8 +173,6 @@ public abstract class XBaseMenuWindow extends XWindow {
     XBaseMenuWindow() {
         super(new XCreateWindowParams(new Object[] {
             DELAYED, Boolean.TRUE}));
-
-        disposeAppContext = AppContext.getAppContext();
     }
 
     /************************************************
@@ -920,7 +916,7 @@ public abstract class XBaseMenuWindow extends XWindow {
     public void dispose() {
         setDisposed(true);
 
-        SunToolkit.invokeLaterOnAppContext(disposeAppContext, new Runnable()  {
+        SunToolkit.invokeLater(new Runnable()  {
             public void run() {
                 doDispose();
             }
diff --git a/src/java.desktop/unix/classes/sun/awt/X11/XEmbedChildProxyPeer.java b/src/java.desktop/unix/classes/sun/awt/X11/XEmbedChildProxyPeer.java
index efae47d6f23..03593761111 100644
--- a/src/java.desktop/unix/classes/sun/awt/X11/XEmbedChildProxyPeer.java
+++ b/src/java.desktop/unix/classes/sun/awt/X11/XEmbedChildProxyPeer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2026, 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
@@ -201,7 +201,7 @@ public final class XEmbedChildProxyPeer implements ComponentPeer, XEventDispatch
     public void                 updateCursorImmediately() {}
 
     void postEvent(AWTEvent event) {
-        XToolkit.postEvent(XToolkit.targetToAppContext(proxy), event);
+        XToolkit.postEvent(event);
     }
 
     boolean simulateMotifRequestFocus(Component lightweightChild, boolean temporary,
@@ -323,9 +323,9 @@ public final class XEmbedChildProxyPeer implements ComponentPeer, XEventDispatch
     }
 
     void childResized() {
-        XToolkit.postEvent(XToolkit.targetToAppContext(proxy), new ComponentEvent(proxy, ComponentEvent.COMPONENT_RESIZED));
+        XToolkit.postEvent(new ComponentEvent(proxy, ComponentEvent.COMPONENT_RESIZED));
         container.childResized(proxy);
-//         XToolkit.postEvent(XToolkit.targetToAppContext(proxy), new InvocationEvent(proxy, new Runnable() {
+//         XToolkit.postEvent(new InvocationEvent(proxy, new Runnable() {
 //                 public void run() {
 //                     getTopLevel(proxy).invalidate();
 //                     getTopLevel(proxy).pack();
diff --git a/src/java.desktop/unix/classes/sun/awt/X11/XTaskbarPeer.java b/src/java.desktop/unix/classes/sun/awt/X11/XTaskbarPeer.java
index 7f0629e101e..cb80f0abd0e 100644
--- a/src/java.desktop/unix/classes/sun/awt/X11/XTaskbarPeer.java
+++ b/src/java.desktop/unix/classes/sun/awt/X11/XTaskbarPeer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2026, 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
@@ -152,7 +152,7 @@ final class XTaskbarPeer implements TaskbarPeer {
                     mi.getActionCommand());
             try {
                 XToolkit.awtLock();
-                XToolkit.postEvent(XToolkit.targetToAppContext(ae.getSource()), ae);
+                XToolkit.postEvent(ae);
             } finally {
                 XToolkit.awtUnlock();
             }
diff --git a/src/java.desktop/unix/classes/sun/awt/X11/XToolkit.java b/src/java.desktop/unix/classes/sun/awt/X11/XToolkit.java
index 5dcd1b763e1..1ec0039febd 100644
--- a/src/java.desktop/unix/classes/sun/awt/X11/XToolkit.java
+++ b/src/java.desktop/unix/classes/sun/awt/X11/XToolkit.java
@@ -625,14 +625,8 @@ public final class XToolkit extends UNIXToolkit implements Runnable {
         while(true) {
             // Fix for 6829923: we should gracefully handle toolkit thread interruption
             if (Thread.currentThread().isInterrupted()) {
-                // We expect interruption from the AppContext.dispose() method only.
                 // If the thread is interrupted from another place, let's skip it
-                // for compatibility reasons. Probably some time later we'll remove
-                // the check for AppContext.isDisposed() and will unconditionally
-                // break the loop here.
-                if (AppContext.getAppContext().isDisposed()) {
-                    break;
-                }
+                // for compatibility reasons.
             }
             awtLock();
             try {
@@ -2054,14 +2048,6 @@ public final class XToolkit extends UNIXToolkit implements Runnable {
                (exclusionType == Dialog.ModalExclusionType.TOOLKIT_EXCLUDE);
     }
 
-    static EventQueue getEventQueue(Object target) {
-        AppContext appContext = targetToAppContext(target);
-        if (appContext != null) {
-            return (EventQueue)appContext.get(AppContext.EVENT_QUEUE_KEY);
-        }
-        return null;
-    }
-
     static void removeSourceEvents(EventQueue queue,
                                    Object source,
                                    boolean removeAllEvents) {
diff --git a/src/java.desktop/unix/classes/sun/awt/X11/XTrayIconPeer.java b/src/java.desktop/unix/classes/sun/awt/X11/XTrayIconPeer.java
index 9f0ac241f5b..5ab97125991 100644
--- a/src/java.desktop/unix/classes/sun/awt/X11/XTrayIconPeer.java
+++ b/src/java.desktop/unix/classes/sun/awt/X11/XTrayIconPeer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2026, 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
@@ -269,7 +269,7 @@ public final class XTrayIconPeer implements TrayIconPeer,
 
     @Override
     public void dispose() {
-        if (SunToolkit.isDispatchThreadForAppContext(target)) {
+        if (EventQueue.isDispatchThread()) {
             disposeOnEDT();
         } else {
             try {
@@ -329,7 +329,7 @@ public final class XTrayIconPeer implements TrayIconPeer,
                 }
             };
 
-        if (!SunToolkit.isDispatchThreadForAppContext(target)) {
+        if (!EventQueue.isDispatchThread()) {
             SunToolkit.executeOnEventHandlerThread(target, r);
         } else {
             r.run();
@@ -355,7 +355,7 @@ public final class XTrayIconPeer implements TrayIconPeer,
         if (isDisposed())
             return;
 
-        assert SunToolkit.isDispatchThreadForAppContext(target);
+        assert EventQueue.isDispatchThread();
 
         PopupMenu newPopup = target.getPopupMenu();
         if (popup != newPopup) {
@@ -476,7 +476,7 @@ public final class XTrayIconPeer implements TrayIconPeer,
             // other class tries to cast source field to Component).
             // We already filter DRAG events out (CR 6565779).
             e.setSource(xtiPeer.target);
-            XToolkit.postEvent(XToolkit.targetToAppContext(e.getSource()), e);
+            XToolkit.postEvent(e);
         }
         @Override
         @SuppressWarnings("deprecation")
@@ -487,7 +487,7 @@ public final class XTrayIconPeer implements TrayIconPeer,
                 ActionEvent aev = new ActionEvent(xtiPeer.target, ActionEvent.ACTION_PERFORMED,
                                                   xtiPeer.target.getActionCommand(), e.getWhen(),
                                                   e.getModifiers());
-                XToolkit.postEvent(XToolkit.targetToAppContext(aev.getSource()), aev);
+                XToolkit.postEvent(aev);
             }
             if (xtiPeer.balloon.isVisible()) {
                 xtiPeer.balloon.hide();
diff --git a/src/java.desktop/unix/classes/sun/awt/X11InputMethodBase.java b/src/java.desktop/unix/classes/sun/awt/X11InputMethodBase.java
index d9eaa9629d1..3aa1624a1dd 100644
--- a/src/java.desktop/unix/classes/sun/awt/X11InputMethodBase.java
+++ b/src/java.desktop/unix/classes/sun/awt/X11InputMethodBase.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2026, 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
@@ -434,7 +434,7 @@ public abstract class X11InputMethodBase extends InputMethodAdapter {
         if (source != null) {
             InputMethodEvent event = new InputMethodEvent(source,
                 id, when, text, committedCharacterCount, caret, visiblePosition);
-            SunToolkit.postEvent(SunToolkit.targetToAppContext(source), (AWTEvent)event);
+            SunToolkit.postEvent((AWTEvent)event);
         }
     }
 
diff --git a/src/java.desktop/unix/native/common/awt/awt_Component.h b/src/java.desktop/unix/native/common/awt/awt_Component.h
index 089691a8a4d..7ef0a0de94b 100644
--- a/src/java.desktop/unix/native/common/awt/awt_Component.h
+++ b/src/java.desktop/unix/native/common/awt/awt_Component.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2026, 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
@@ -38,7 +38,6 @@ struct ComponentIDs {
     jfieldID graphicsConfig;
     jfieldID name;
     jfieldID isProxyActive;
-    jfieldID appContext;
     jmethodID getParent;
     jmethodID getLocationOnScreen;
 };
diff --git a/src/java.desktop/unix/native/libawt/awt/awt_LoadLibrary.c b/src/java.desktop/unix/native/libawt/awt/awt_LoadLibrary.c
index cd07c347c9e..af50fdbb5c0 100644
--- a/src/java.desktop/unix/native/libawt/awt/awt_LoadLibrary.c
+++ b/src/java.desktop/unix/native/libawt/awt/awt_LoadLibrary.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2026, 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
@@ -36,7 +36,7 @@
 #include 
 
 #ifdef AIX
-#include "porting_aix.h" /* For the 'dladdr' function. */
+#define dladdr JVM_dladdr
 #endif
 
 #ifdef DEBUG
diff --git a/src/java.desktop/unix/native/libawt_xawt/xawt/XToolkit.c b/src/java.desktop/unix/native/libawt_xawt/xawt/XToolkit.c
index d5f3130386d..c84aa4ff8f9 100644
--- a/src/java.desktop/unix/native/libawt_xawt/xawt/XToolkit.c
+++ b/src/java.desktop/unix/native/libawt_xawt/xawt/XToolkit.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2026, 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
@@ -195,10 +195,6 @@ Java_java_awt_Component_initIDs
                            "Z");
     CHECK_NULL(componentIDs.isProxyActive);
 
-    componentIDs.appContext =
-        (*env)->GetFieldID(env, cls, "appContext",
-                           "Lsun/awt/AppContext;");
-
     (*env)->DeleteLocalRef(env, keyclass);
 }
 
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsBorders.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsBorders.java
index 81766db8116..572e9e8c117 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsBorders.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsBorders.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2026, 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
@@ -115,7 +115,7 @@ public final class WindowsBorders {
     }
 
     @SuppressWarnings("serial") // Superclass is not serializable across versions
-    public static final class ProgressBarBorder extends AbstractBorder implements UIResource {
+    public static class ProgressBarBorder extends AbstractBorder implements UIResource {
         protected Color shadow;
         protected Color highlight;
 
@@ -148,7 +148,7 @@ public final class WindowsBorders {
      * @since 1.4
      */
     @SuppressWarnings("serial") // Superclass is not serializable across versions
-    public static final class ToolBarBorder extends AbstractBorder implements UIResource, SwingConstants {
+    public static class ToolBarBorder extends AbstractBorder implements UIResource, SwingConstants {
         protected Color shadow;
         protected Color highlight;
 
@@ -308,7 +308,7 @@ public final class WindowsBorders {
      * @since 1.4
      */
     @SuppressWarnings("serial") // Superclass is not serializable across versions
-    public static final class InternalFrameLineBorder extends LineBorder implements
+    public static class InternalFrameLineBorder extends LineBorder implements
             UIResource {
         protected Color activeColor;
         protected Color inactiveColor;
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsButtonUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsButtonUI.java
index ee07276aa30..63e99e0804a 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsButtonUI.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsButtonUI.java
@@ -56,7 +56,7 @@ import static com.sun.java.swing.plaf.windows.XPStyle.Skin;
  *
  * @author Jeff Dinkins
  */
-public final class WindowsButtonUI extends BasicButtonUI
+public class WindowsButtonUI extends BasicButtonUI
 {
     protected int dashedRectGapX;
     protected int dashedRectGapY;
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxMenuItemUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxMenuItemUI.java
index 3a2578b3e0b..47e311486ba 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxMenuItemUI.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxMenuItemUI.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2026, 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
@@ -42,7 +42,7 @@ import com.sun.java.swing.plaf.windows.TMSchema.State;
 /**
  * Windows check box menu item.
  */
-public final class WindowsCheckBoxMenuItemUI extends BasicCheckBoxMenuItemUI {
+public class WindowsCheckBoxMenuItemUI extends BasicCheckBoxMenuItemUI {
 
     final WindowsMenuItemUIAccessor accessor =
         new WindowsMenuItemUIAccessor() {
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxUI.java
index 7cb2490fd76..d264393f4d8 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxUI.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxUI.java
@@ -35,7 +35,7 @@ import javax.swing.plaf.ComponentUI;
  *
  * @author Jeff Dinkins
  */
-public final class WindowsCheckBoxUI extends WindowsRadioButtonUI
+public class WindowsCheckBoxUI extends WindowsRadioButtonUI
 {
     // NOTE: WindowsCheckBoxUI inherits from WindowsRadioButtonUI instead
     // of BasicCheckBoxUI because we want to pick up all the
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsClassicLookAndFeel.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsClassicLookAndFeel.java
index 5716a875cd7..802b5f66888 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsClassicLookAndFeel.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsClassicLookAndFeel.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2026, 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
@@ -31,7 +31,7 @@ package com.sun.java.swing.plaf.windows;
  * @since 1.5
  */
 @SuppressWarnings("serial") // Superclass is not serializable across versions
-public final class WindowsClassicLookAndFeel extends WindowsLookAndFeel {
+public class WindowsClassicLookAndFeel extends WindowsLookAndFeel {
     @Override
     public String getName() {
         return "Windows Classic";
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsComboBoxUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsComboBoxUI.java
index fdc9b03ae7d..8717fd715ea 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsComboBoxUI.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsComboBoxUI.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2026, 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
@@ -75,7 +75,7 @@ import static com.sun.java.swing.plaf.windows.XPStyle.Skin;
  * @author Tom Santos
  * @author Igor Kushnirskiy
  */
-public final class WindowsComboBoxUI extends BasicComboBoxUI {
+public class WindowsComboBoxUI extends BasicComboBoxUI {
 
     private static final MouseListener rolloverListener =
         new MouseAdapter() {
@@ -532,7 +532,7 @@ public final class WindowsComboBoxUI extends BasicComboBoxUI {
     }
 
     @SuppressWarnings("serial") // Same-version serialization only
-    protected final class WinComboPopUp extends BasicComboPopup {
+    protected class WinComboPopUp extends BasicComboPopup {
         private Skin listBoxBorder = null;
         private XPStyle xp;
 
@@ -550,7 +550,7 @@ public final class WindowsComboBoxUI extends BasicComboBoxUI {
             return new InvocationKeyHandler();
         }
 
-        protected final class InvocationKeyHandler extends BasicComboPopup.InvocationKeyHandler {
+        protected class InvocationKeyHandler extends BasicComboPopup.InvocationKeyHandler {
             protected InvocationKeyHandler() {
                 WinComboPopUp.this.super();
             }
@@ -570,7 +570,7 @@ public final class WindowsComboBoxUI extends BasicComboBoxUI {
     /**
      * Subclassed to highlight selected item in an editable combo box.
      */
-    public static final class WindowsComboBoxEditor
+    public static class WindowsComboBoxEditor
         extends BasicComboBoxEditor.UIResource {
 
         /**
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopIconUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopIconUI.java
index 47ecdf5747b..2cebb050396 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopIconUI.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopIconUI.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2026, 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
@@ -36,7 +36,7 @@ import javax.swing.plaf.basic.BasicDesktopIconUI;
 /**
  * Windows icon for a minimized window on the desktop.
  */
-public final class WindowsDesktopIconUI extends BasicDesktopIconUI {
+public class WindowsDesktopIconUI extends BasicDesktopIconUI {
     private int width;
 
     public static ComponentUI createUI(JComponent c) {
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopManager.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopManager.java
index 79d81bad089..ae081a7690c 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopManager.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopManager.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2026, 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
@@ -52,7 +52,7 @@ import java.lang.ref.WeakReference;
  * @author Thomas Ball
  */
 @SuppressWarnings("serial") // JDK-implementation class
-public final class WindowsDesktopManager extends DefaultDesktopManager
+public class WindowsDesktopManager extends DefaultDesktopManager
         implements java.io.Serializable, javax.swing.plaf.UIResource {
 
     /* The frame which is currently selected/activated.
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopPaneUI.java
index dabbe3fb992..4a3f0ec38b1 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopPaneUI.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopPaneUI.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2026, 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,7 +34,7 @@ import javax.swing.plaf.basic.BasicDesktopPaneUI;
  *
  * @author David Kloba
  */
-public final class WindowsDesktopPaneUI extends BasicDesktopPaneUI
+public class WindowsDesktopPaneUI extends BasicDesktopPaneUI
 {
     public static ComponentUI createUI(JComponent c) {
         return new WindowsDesktopPaneUI();
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsEditorPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsEditorPaneUI.java
index 44cb0e9634c..ea21b41c619 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsEditorPaneUI.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsEditorPaneUI.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2026, 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
@@ -33,7 +33,7 @@ import javax.swing.text.Caret;
 /**
  * Windows rendition of the component.
  */
-public final class WindowsEditorPaneUI extends BasicEditorPaneUI
+public class WindowsEditorPaneUI extends BasicEditorPaneUI
 {
 
     /**
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsFileChooserUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsFileChooserUI.java
index 08c01760be9..86c40ea70d6 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsFileChooserUI.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsFileChooserUI.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2026, 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
@@ -101,7 +101,7 @@ import sun.swing.WindowsPlacesBar;
  *
  * @author Jeff Dinkins
  */
-public final class WindowsFileChooserUI extends BasicFileChooserUI {
+public class WindowsFileChooserUI extends BasicFileChooserUI {
 
     // The following are private because the implementation of the
     // Windows FileChooser L&F is not complete yet.
@@ -1122,7 +1122,7 @@ public final class WindowsFileChooserUI extends BasicFileChooserUI {
      * Data model for a type-face selection combo-box.
      */
     @SuppressWarnings("serial") // Superclass is not serializable across versions
-    protected final class DirectoryComboBoxModel extends AbstractListModel implements ComboBoxModel {
+    protected class DirectoryComboBoxModel extends AbstractListModel implements ComboBoxModel {
         Vector directories = new Vector();
         int[] depths = null;
         File selectedDirectory = null;
@@ -1252,7 +1252,7 @@ public final class WindowsFileChooserUI extends BasicFileChooserUI {
      * Render different type sizes and styles.
      */
     @SuppressWarnings("serial") // Superclass is not serializable across versions
-    public final class FilterComboBoxRenderer extends DefaultListCellRenderer {
+    public class FilterComboBoxRenderer extends DefaultListCellRenderer {
         @Override
         public Component getListCellRendererComponent(JList list,
             Object value, int index, boolean isSelected,
@@ -1279,7 +1279,7 @@ public final class WindowsFileChooserUI extends BasicFileChooserUI {
      * Data model for a type-face selection combo-box.
      */
     @SuppressWarnings("serial") // Superclass is not serializable across versions
-    protected final class FilterComboBoxModel extends AbstractListModel implements ComboBoxModel,
+    protected class FilterComboBoxModel extends AbstractListModel implements ComboBoxModel,
             PropertyChangeListener {
         protected FileFilter[] filters;
         protected FilterComboBoxModel() {
@@ -1362,7 +1362,7 @@ public final class WindowsFileChooserUI extends BasicFileChooserUI {
     /**
      * Acts when DirectoryComboBox has changed the selected item.
      */
-    protected final class DirectoryComboBoxAction implements ActionListener {
+    protected class DirectoryComboBoxAction implements ActionListener {
 
 
 
@@ -1387,7 +1387,7 @@ public final class WindowsFileChooserUI extends BasicFileChooserUI {
     // ***********************
     // * FileView operations *
     // ***********************
-    protected final class WindowsFileView extends BasicFileView {
+    protected class WindowsFileView extends BasicFileView {
         /* FileView type descriptions */
 
         @Override
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameTitlePane.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameTitlePane.java
index ba4bde12122..029e139fe8f 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameTitlePane.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameTitlePane.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2026, 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
@@ -418,7 +418,7 @@ public class WindowsInternalFrameTitlePane extends BasicInternalFrameTitlePane {
         return new WindowsTitlePaneLayout();
     }
 
-    public final class WindowsTitlePaneLayout extends BasicInternalFrameTitlePane.TitlePaneLayout {
+    public class WindowsTitlePaneLayout extends BasicInternalFrameTitlePane.TitlePaneLayout {
         private Insets captionMargin = null;
         private Insets contentMargin = null;
         private XPStyle xp = XPStyle.getXP();
@@ -506,7 +506,7 @@ public class WindowsInternalFrameTitlePane extends BasicInternalFrameTitlePane {
         }
     } // end WindowsTitlePaneLayout
 
-    public final class WindowsPropertyChangeHandler extends PropertyChangeHandler {
+    public class WindowsPropertyChangeHandler extends PropertyChangeHandler {
         @Override
         public void propertyChange(PropertyChangeEvent evt) {
             String prop = evt.getPropertyName();
@@ -530,7 +530,7 @@ public class WindowsInternalFrameTitlePane extends BasicInternalFrameTitlePane {
      * 

* Note: We assume here that icons are square. */ - public static final class ScalableIconUIResource implements Icon, UIResource { + public static class ScalableIconUIResource implements Icon, UIResource { // We can use an arbitrary size here because we scale to it in paintIcon() private static final int SIZE = 16; diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameUI.java index 9db31ba38f9..6e76ac6a5b4 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -45,7 +45,7 @@ import static com.sun.java.swing.plaf.windows.XPStyle.Skin; /** * Windows rendition of the component. */ -public final class WindowsInternalFrameUI extends BasicInternalFrameUI +public class WindowsInternalFrameUI extends BasicInternalFrameUI { XPStyle xp = XPStyle.getXP(); diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLabelUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLabelUI.java index 4283f743b97..c910b635491 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLabelUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLabelUI.java @@ -40,7 +40,7 @@ import sun.swing.SwingUtilities2; /** * Windows rendition of the component. */ -public final class WindowsLabelUI extends BasicLabelUI { +public class WindowsLabelUI extends BasicLabelUI { private static final ComponentUI UI = new WindowsLabelUI(); diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuBarUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuBarUI.java index 3d3cf5feee7..ac26dcbf425 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuBarUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuBarUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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,7 @@ import sun.swing.MnemonicHandler; /** * Windows rendition of the component. */ -public final class WindowsMenuBarUI extends BasicMenuBarUI +public class WindowsMenuBarUI extends BasicMenuBarUI { /* to be accessed on the EDT only */ private WindowListener windowListener = null; diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java index d15bc93a628..d50540588fb 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java @@ -58,7 +58,7 @@ import sun.swing.SwingUtilities2; * * @author Igor Kushnirskiy */ -public final class WindowsMenuItemUI extends BasicMenuItemUI { +public class WindowsMenuItemUI extends BasicMenuItemUI { /** * The instance of {@code PropertyChangeListener}. */ diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java index 259c32c74f4..78028db7c00 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -50,7 +50,7 @@ import com.sun.java.swing.plaf.windows.TMSchema.State; /** * Windows rendition of the component. */ -public final class WindowsMenuUI extends BasicMenuUI { +public class WindowsMenuUI extends BasicMenuUI { protected Integer menuBarHeight; protected boolean hotTrackingOn; @@ -283,7 +283,7 @@ public final class WindowsMenuUI extends BasicMenuUI { * true when the mouse enters the menu and false when it exits. * @since 1.4 */ - protected final class WindowsMouseInputHandler extends BasicMenuUI.MouseInputHandler { + protected class WindowsMouseInputHandler extends BasicMenuUI.MouseInputHandler { @Override public void mouseEntered(MouseEvent evt) { super.mouseEntered(evt); diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsOptionPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsOptionPaneUI.java index 05c1b177705..3bed1856a55 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsOptionPaneUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsOptionPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -30,5 +30,5 @@ import javax.swing.plaf.basic.BasicOptionPaneUI; /** * Windows rendition of the component. */ -public final class WindowsOptionPaneUI extends BasicOptionPaneUI { +public class WindowsOptionPaneUI extends BasicOptionPaneUI { } diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPasswordFieldUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPasswordFieldUI.java index 6adf6e402ec..0c30b291648 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPasswordFieldUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPasswordFieldUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -33,7 +33,7 @@ import javax.swing.text.Caret; /** * Windows rendition of the component. */ -public final class WindowsPasswordFieldUI extends BasicPasswordFieldUI { +public class WindowsPasswordFieldUI extends BasicPasswordFieldUI { /** * Creates a UI for a JPasswordField diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuSeparatorUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuSeparatorUI.java index 576549ae482..f236c6b14fc 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuSeparatorUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuSeparatorUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2026, 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 @@ -42,7 +42,7 @@ import com.sun.java.swing.plaf.windows.XPStyle.Skin; * @author Igor Kushnirskiy */ -public final class WindowsPopupMenuSeparatorUI extends BasicPopupMenuSeparatorUI { +public class WindowsPopupMenuSeparatorUI extends BasicPopupMenuSeparatorUI { public static ComponentUI createUI(JComponent c) { return new WindowsPopupMenuSeparatorUI(); diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuUI.java index 1361286df4a..1c85cfebd94 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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,7 @@ import static sun.swing.SwingUtilities2.BASICMENUITEMUI_MAX_TEXT_OFFSET; * * @author Igor Kushnirskiy */ -public final class WindowsPopupMenuUI extends BasicPopupMenuUI { +public class WindowsPopupMenuUI extends BasicPopupMenuUI { static MnemonicListener mnemonicListener = null; static final Object GUTTER_OFFSET_KEY = diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsProgressBarUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsProgressBarUI.java index 9cc7d277ff1..5440b98cd1b 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsProgressBarUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsProgressBarUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -51,7 +51,7 @@ import static com.sun.java.swing.plaf.windows.XPStyle.Skin; * * @author Michael C. Albers */ -public final class WindowsProgressBarUI extends BasicProgressBarUI +public class WindowsProgressBarUI extends BasicProgressBarUI { private Rectangle previousFullBox; diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonMenuItemUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonMenuItemUI.java index 78768c29ab3..2ec78341c2a 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonMenuItemUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonMenuItemUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -42,7 +42,7 @@ import com.sun.java.swing.plaf.windows.TMSchema.State; /** * Windows rendition of the component. */ -public final class WindowsRadioButtonMenuItemUI extends BasicRadioButtonMenuItemUI { +public class WindowsRadioButtonMenuItemUI extends BasicRadioButtonMenuItemUI { final WindowsMenuItemUIAccessor accessor = new WindowsMenuItemUIAccessor() { diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRootPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRootPaneUI.java index 5e08dcf5605..d41fd9421e4 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRootPaneUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRootPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -70,7 +70,7 @@ import sun.swing.MnemonicHandler; * @author Mark Davidson * @since 1.4 */ -public final class WindowsRootPaneUI extends BasicRootPaneUI { +public class WindowsRootPaneUI extends BasicRootPaneUI { private static final WindowsRootPaneUI windowsRootPaneUI = new WindowsRootPaneUI(); static final AltProcessor altProcessor = new AltProcessor(); diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollBarUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollBarUI.java index 04a9f2e97cf..2755f3543f1 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollBarUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollBarUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -55,7 +55,7 @@ import static com.sun.java.swing.plaf.windows.XPStyle.Skin; /** * Windows rendition of the component. */ -public final class WindowsScrollBarUI extends BasicScrollBarUI { +public class WindowsScrollBarUI extends BasicScrollBarUI { private Grid thumbGrid; private Grid highlightGrid; private Dimension horizontalThumbSize; diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollPaneUI.java index 48e7a8c02fb..56b8eb1004e 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollPaneUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -30,5 +30,5 @@ import javax.swing.plaf.basic.BasicScrollPaneUI; /** * Windows rendition of the component. */ -public final class WindowsScrollPaneUI extends BasicScrollPaneUI +public class WindowsScrollPaneUI extends BasicScrollPaneUI {} diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSeparatorUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSeparatorUI.java index 2a2caef60d2..12eaa33872c 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSeparatorUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSeparatorUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2026, 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 @@ -30,4 +30,4 @@ import javax.swing.plaf.basic.*; /** * Windows Separator. */ -public final class WindowsSeparatorUI extends BasicSeparatorUI { } +public class WindowsSeparatorUI extends BasicSeparatorUI { } diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSliderUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSliderUI.java index 731775a2575..cfc509babf4 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSliderUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSliderUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -44,7 +44,7 @@ import static com.sun.java.swing.plaf.windows.XPStyle.Skin; /** * Windows rendition of the component. */ -public final class WindowsSliderUI extends BasicSliderUI +public class WindowsSliderUI extends BasicSliderUI { private boolean rollover = false; private boolean pressed = false; diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSpinnerUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSpinnerUI.java index a8e2a2ddcf1..8934bf9ff21 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSpinnerUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSpinnerUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -37,7 +37,7 @@ import static com.sun.java.swing.plaf.windows.TMSchema.State; import static com.sun.java.swing.plaf.windows.XPStyle.Skin; -public final class WindowsSpinnerUI extends BasicSpinnerUI { +public class WindowsSpinnerUI extends BasicSpinnerUI { public static ComponentUI createUI(JComponent c) { return new WindowsSpinnerUI(); } diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneDivider.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneDivider.java index a132756bbee..26cd1bd8c2d 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneDivider.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneDivider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, 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 @@ -39,7 +39,7 @@ import javax.swing.plaf.basic.BasicSplitPaneUI; * @author Jeff Dinkins */ @SuppressWarnings("serial") // Superclass is not serializable across versions -public final class WindowsSplitPaneDivider extends BasicSplitPaneDivider +public class WindowsSplitPaneDivider extends BasicSplitPaneDivider { /** diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneUI.java index 481fa466a5b..b67ab22f48f 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -33,7 +33,7 @@ import javax.swing.plaf.basic.BasicSplitPaneUI; /** * Windows rendition of the component. */ -public final class WindowsSplitPaneUI extends BasicSplitPaneUI +public class WindowsSplitPaneUI extends BasicSplitPaneUI { public WindowsSplitPaneUI() { diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTabbedPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTabbedPaneUI.java index 874b5c65c6e..da8e8b9d385 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTabbedPaneUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTabbedPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -48,7 +48,7 @@ import static com.sun.java.swing.plaf.windows.XPStyle.Skin; /** * Windows rendition of the component. */ -public final class WindowsTabbedPaneUI extends BasicTabbedPaneUI { +public class WindowsTabbedPaneUI extends BasicTabbedPaneUI { /** * Keys to use for forward focus traversal when the JComponent is * managing focus. diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTableHeaderUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTableHeaderUI.java index de8f18b4ea1..1db0050f162 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTableHeaderUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTableHeaderUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -50,7 +50,7 @@ import static com.sun.java.swing.plaf.windows.TMSchema.Part; import static com.sun.java.swing.plaf.windows.TMSchema.State; import static com.sun.java.swing.plaf.windows.XPStyle.Skin; -public final class WindowsTableHeaderUI extends BasicTableHeaderUI { +public class WindowsTableHeaderUI extends BasicTableHeaderUI { private TableCellRenderer originalHeaderRenderer; public static ComponentUI createUI(JComponent h) { diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextAreaUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextAreaUI.java index 78cceff2a0c..7c9abb12e05 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextAreaUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextAreaUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -33,7 +33,7 @@ import javax.swing.text.Caret; /** * Windows rendition of the component. */ -public final class WindowsTextAreaUI extends BasicTextAreaUI { +public class WindowsTextAreaUI extends BasicTextAreaUI { /** * Creates the object to use for a caret. By default an * instance of WindowsCaret is created. This method diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextFieldUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextFieldUI.java index 5846dcb9f09..9920ed371d8 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextFieldUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextFieldUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -62,7 +62,7 @@ import javax.swing.text.Position; * * @author Timothy Prinzing */ -public final class WindowsTextFieldUI extends BasicTextFieldUI +public class WindowsTextFieldUI extends BasicTextFieldUI { /** * Creates a UI for a JTextField. diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextPaneUI.java index 2c645903e51..d1418205385 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextPaneUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -33,7 +33,7 @@ import javax.swing.text.Caret; /** * Windows rendition of the component. */ -public final class WindowsTextPaneUI extends BasicTextPaneUI +public class WindowsTextPaneUI extends BasicTextPaneUI { /** * Creates a UI for a JTextPane. diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToggleButtonUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToggleButtonUI.java index 67eb5c1d6a0..a612b3f392e 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToggleButtonUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToggleButtonUI.java @@ -43,7 +43,7 @@ import javax.swing.plaf.basic.BasicToggleButtonUI; * * @author Jeff Dinkins */ -public final class WindowsToggleButtonUI extends BasicToggleButtonUI +public class WindowsToggleButtonUI extends BasicToggleButtonUI { protected int dashedRectGapX; protected int dashedRectGapY; diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToolBarSeparatorUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToolBarSeparatorUI.java index 47175b83d30..1707ce5a80c 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToolBarSeparatorUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToolBarSeparatorUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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,7 +40,7 @@ import static com.sun.java.swing.plaf.windows.XPStyle.Skin; * * @author Mark Davidson */ -public final class WindowsToolBarSeparatorUI extends BasicToolBarSeparatorUI { +public class WindowsToolBarSeparatorUI extends BasicToolBarSeparatorUI { public static ComponentUI createUI( JComponent c ) { return new WindowsToolBarSeparatorUI(); diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToolBarUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToolBarUI.java index 025c30c5c96..4e2cf42bf5d 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToolBarUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToolBarUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -45,7 +45,7 @@ import javax.swing.plaf.basic.BasicToolBarUI; import static com.sun.java.swing.plaf.windows.TMSchema.Part; -public final class WindowsToolBarUI extends BasicToolBarUI { +public class WindowsToolBarUI extends BasicToolBarUI { public static ComponentUI createUI(JComponent c) { return new WindowsToolBarUI(); diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTreeUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTreeUI.java index 78384bbd18a..26edfb978bd 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTreeUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTreeUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -167,7 +167,7 @@ public class WindowsTreeUI extends BasicTreeUI { * The plus sign button icon */ @SuppressWarnings("serial") // Superclass is not serializable across versions - public static final class CollapsedIcon extends ExpandedIcon { + public static class CollapsedIcon extends ExpandedIcon { public static Icon createCollapsedIcon() { return new CollapsedIcon(); } @@ -185,7 +185,7 @@ public class WindowsTreeUI extends BasicTreeUI { } @SuppressWarnings("serial") // Superclass is not serializable across versions - public final class WindowsTreeCellRenderer extends DefaultTreeCellRenderer { + public class WindowsTreeCellRenderer extends DefaultTreeCellRenderer { /** * Configures the renderer based on the passed in components. diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Component.cpp b/src/java.desktop/windows/native/libawt/windows/awt_Component.cpp index b67c5dfcf8d..eca8290a1aa 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Component.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_Component.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2026, 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 @@ -178,7 +178,6 @@ jfieldID AwtComponent::parentID; jfieldID AwtComponent::graphicsConfigID; jfieldID AwtComponent::peerGCID; jfieldID AwtComponent::focusableID; -jfieldID AwtComponent::appContextID; jfieldID AwtComponent::cursorID; jfieldID AwtComponent::hwndID; @@ -6573,11 +6572,6 @@ Java_java_awt_Component_initIDs(JNIEnv *env, jclass cls) DASSERT(AwtComponent::focusableID); CHECK_NULL(AwtComponent::focusableID); - AwtComponent::appContextID = env->GetFieldID(cls, "appContext", - "Lsun/awt/AppContext;"); - DASSERT(AwtComponent::appContextID); - CHECK_NULL(AwtComponent::appContextID); - AwtComponent::peerGCID = env->GetFieldID(peerCls, "winGraphicsConfig", "Lsun/awt/Win32GraphicsConfig;"); DASSERT(AwtComponent::peerGCID); diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Component.h b/src/java.desktop/windows/native/libawt/windows/awt_Component.h index 740eb8c72f9..1246f6cb06e 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Component.h +++ b/src/java.desktop/windows/native/libawt/windows/awt_Component.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2026, 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,7 +112,6 @@ public: static jfieldID graphicsConfigID; static jfieldID peerGCID; static jfieldID focusableID; - static jfieldID appContextID; static jfieldID hwndID; static jmethodID getFontMID; diff --git a/src/java.desktop/windows/native/libawt/windows/awt_PrintJob.cpp b/src/java.desktop/windows/native/libawt/windows/awt_PrintJob.cpp index 8d016d8b39f..b18fa5a7e2c 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_PrintJob.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_PrintJob.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2026, 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 @@ -522,7 +522,6 @@ Java_sun_awt_windows_WPageDialogPeer__1show(JNIEnv *env, jobject peer) AwtComponent *awtParent = (parent != NULL) ? (AwtComponent *)JNI_GET_PDATA(parent) : NULL; HWND hwndOwner = awtParent ? awtParent->GetHWnd() : NULL; - jboolean doIt = JNI_FALSE; PAGESETUPDLG setup; memset(&setup, 0, sizeof(setup)); @@ -578,7 +577,7 @@ Java_sun_awt_windows_WPageDialogPeer__1show(JNIEnv *env, jobject peer) */ if ((setup.hDevMode == NULL) && (setup.hDevNames == NULL)) { CLEANUP_SHOW; - return doIt; + return JNI_FALSE; } } else { int measure = PSD_INTHOUSANDTHSOFINCHES; @@ -606,7 +605,7 @@ Java_sun_awt_windows_WPageDialogPeer__1show(JNIEnv *env, jobject peer) pageFormatToSetup(env, self, page, &setup, AwtPrintControl::getPrintDC(env, self)); if (env->ExceptionCheck()) { CLEANUP_SHOW; - return doIt; + return JNI_FALSE; } setup.lpfnPageSetupHook = reinterpret_cast(pageDlgHook); @@ -615,89 +614,91 @@ Java_sun_awt_windows_WPageDialogPeer__1show(JNIEnv *env, jobject peer) AwtDialog::CheckInstallModalHook(); BOOL ret = ::PageSetupDlg(&setup); - if (ret) { - - jobject paper = getPaper(env, page); - if (paper == NULL) { - CLEANUP_SHOW; - return doIt; - } - int units = setup.Flags & PSD_INTHOUSANDTHSOFINCHES ? - MM_HIENGLISH : - MM_HIMETRIC; - POINT paperSize; - RECT margins; - jint orientation; - - /* The printer may have been changed, and we track that change, - * but then need to get a new DC for the current printer so that - * we validate the paper size correctly - */ - if (setup.hDevNames != NULL) { - DEVNAMES* names = (DEVNAMES*)::GlobalLock(setup.hDevNames); - if (names != NULL) { - LPTSTR printer = (LPTSTR)names+names->wDeviceOffset; - SAVE_CONTROLWORD - HDC newDC = ::CreateDC(TEXT("WINSPOOL"), printer, NULL, NULL); - RESTORE_CONTROLWORD - if (newDC != NULL) { - HDC oldDC = AwtPrintControl::getPrintDC(env, self); - if (oldDC != NULL) { - ::DeleteDC(oldDC); - } - } - AwtPrintControl::setPrintDC(env, self, newDC); - } - ::GlobalUnlock(setup.hDevNames); - } - - /* Get the Windows paper and margins description. - */ - retrievePaperInfo(&setup, &paperSize, &margins, &orientation, - AwtPrintControl::getPrintDC(env, self)); - - /* Convert the Windows' paper and margins description - * and place them into a Paper instance. - */ - setPaperValues(env, paper, &paperSize, &margins, units); - if (env->ExceptionCheck()) { - CLEANUP_SHOW; - return doIt; - } - /* - * Put the updated Paper instance and the orientation into - * the PageFormat. - */ - setPaper(env, page, paper); - if (env->ExceptionCheck()) { - CLEANUP_SHOW; - return doIt; - } - setPageFormatOrientation(env, page, orientation); - if (env->ExceptionCheck()) { - CLEANUP_SHOW; - return JNI_FALSE; - } - if (setup.hDevMode != NULL) { - DEVMODE *devmode = (DEVMODE *)::GlobalLock(setup.hDevMode); - if (devmode != NULL) { - if (devmode->dmFields & DM_PAPERSIZE) { - jboolean err = setPrintPaperSize(env, self, devmode->dmPaperSize); - if (err) { - CLEANUP_SHOW; - return doIt; - } - } - } - ::GlobalUnlock(setup.hDevMode); - } - doIt = JNI_TRUE; - } AwtDialog::CheckUninstallModalHook(); - AwtDialog::ModalActivateNextWindow(NULL, target, peer); + if (!ret) { + CLEANUP_SHOW; + return JNI_FALSE; + } + + jobject paper = getPaper(env, page); + if (paper == NULL) { + CLEANUP_SHOW; + return JNI_FALSE; + } + int units = setup.Flags & PSD_INTHOUSANDTHSOFINCHES ? + MM_HIENGLISH : + MM_HIMETRIC; + POINT paperSize; + RECT margins; + jint orientation; + + /* The printer may have been changed, and we track that change, + * but then need to get a new DC for the current printer so that + * we validate the paper size correctly + */ + if (setup.hDevNames != NULL) { + DEVNAMES* names = (DEVNAMES*)::GlobalLock(setup.hDevNames); + if (names != NULL) { + LPTSTR printer = (LPTSTR)names+names->wDeviceOffset; + SAVE_CONTROLWORD + HDC newDC = ::CreateDC(TEXT("WINSPOOL"), printer, NULL, NULL); + RESTORE_CONTROLWORD + if (newDC != NULL) { + HDC oldDC = AwtPrintControl::getPrintDC(env, self); + if (oldDC != NULL) { + ::DeleteDC(oldDC); + } + } + AwtPrintControl::setPrintDC(env, self, newDC); + } + ::GlobalUnlock(setup.hDevNames); + } + + /* Get the Windows paper and margins description. + */ + retrievePaperInfo(&setup, &paperSize, &margins, &orientation, + AwtPrintControl::getPrintDC(env, self)); + + /* Convert the Windows' paper and margins description + * and place them into a Paper instance. + */ + setPaperValues(env, paper, &paperSize, &margins, units); + if (env->ExceptionCheck()) { + CLEANUP_SHOW; + return JNI_FALSE; + } + /* + * Put the updated Paper instance and the orientation into + * the PageFormat. + */ + setPaper(env, page, paper); + if (env->ExceptionCheck()) { + CLEANUP_SHOW; + return JNI_FALSE; + } + setPageFormatOrientation(env, page, orientation); + if (env->ExceptionCheck()) { + CLEANUP_SHOW; + return JNI_FALSE; + } + if (setup.hDevMode != NULL) { + DEVMODE *devmode = (DEVMODE *)::GlobalLock(setup.hDevMode); + if (devmode != NULL) { + if (devmode->dmFields & DM_PAPERSIZE) { + jboolean err = setPrintPaperSize(env, self, devmode->dmPaperSize); + if (err) { + ::GlobalUnlock(setup.hDevMode); + CLEANUP_SHOW; + return JNI_FALSE; + } + } + } + ::GlobalUnlock(setup.hDevMode); + } + HGLOBAL oldG = AwtPrintControl::getPrintHDMode(env, self); if (setup.hDevMode != oldG) { AwtPrintControl::setPrintHDMode(env, self, setup.hDevMode); @@ -710,7 +711,7 @@ Java_sun_awt_windows_WPageDialogPeer__1show(JNIEnv *env, jobject peer) CLEANUP_SHOW; - return doIt; + return JNI_TRUE; CATCH_BAD_ALLOC_RET(0); } diff --git a/src/java.naming/share/classes/com/sun/jndi/ldap/Connection.java b/src/java.naming/share/classes/com/sun/jndi/ldap/Connection.java index dcb739a8697..1e0a924f12c 100644 --- a/src/java.naming/share/classes/com/sun/jndi/ldap/Connection.java +++ b/src/java.naming/share/classes/com/sun/jndi/ldap/Connection.java @@ -57,6 +57,8 @@ import javax.net.ssl.HandshakeCompletedListener; import javax.net.ssl.SSLPeerUnverifiedException; import javax.security.sasl.SaslException; +import jdk.internal.misc.InnocuousThread; + /** * A thread that creates a connection to an LDAP server. * After the connection, the thread reads from the connection. @@ -112,9 +114,6 @@ import javax.security.sasl.SaslException; * for v2. * %%% made public for access by LdapSasl %%% * - * @author Vincent Ryan - * @author Rosanna Lee - * @author Jagane Sundar */ public final class Connection implements Runnable { @@ -254,7 +253,7 @@ public final class Connection implements Runnable { throw ce; } - worker = new Thread(this); + worker = InnocuousThread.newSystemThread("LDAP Connection", this); worker.setDaemon(true); worker.start(); } @@ -912,7 +911,7 @@ public final class Connection implements Runnable { // //////////////////////////////////////////////////////////////////////////// - + @Override public void run() { byte inbuf[]; // Buffer for reading incoming bytes int inMsgId; // Message id of incoming response diff --git a/src/java.naming/share/classes/com/sun/jndi/ldap/EventQueue.java b/src/java.naming/share/classes/com/sun/jndi/ldap/EventQueue.java index 4f1cb9ec6a7..7d45d058c68 100644 --- a/src/java.naming/share/classes/com/sun/jndi/ldap/EventQueue.java +++ b/src/java.naming/share/classes/com/sun/jndi/ldap/EventQueue.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, 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 @@ -36,13 +36,13 @@ import javax.naming.event.NamingListener; import javax.naming.ldap.UnsolicitedNotificationEvent; import javax.naming.ldap.UnsolicitedNotificationListener; +import jdk.internal.misc.InnocuousThread; + /** * Package private class used by EventSupport to dispatch events. * This class implements an event queue, and a dispatcher thread that * dequeues and dispatches events from the queue. * - * Pieces stolen from sun.misc.Queue. - * * @author Bill Shannon (from javax.mail.event) * @author Rosanna Lee (modified for JNDI-related events) */ @@ -71,7 +71,7 @@ final class EventQueue implements Runnable { // package private EventQueue() { - qThread = new Thread(this); + qThread = InnocuousThread.newSystemThread("LDAP Event Dispatcher", this); qThread.setDaemon(true); // not a user thread qThread.start(); } @@ -141,6 +141,7 @@ final class EventQueue implements Runnable { /** * Pull events off the queue and dispatch them. */ + @Override public void run() { QueueElement qe; diff --git a/src/java.naming/share/classes/com/sun/jndi/ldap/NamingEventNotifier.java b/src/java.naming/share/classes/com/sun/jndi/ldap/NamingEventNotifier.java index 40a8173b768..0e30c1c1d38 100644 --- a/src/java.naming/share/classes/com/sun/jndi/ldap/NamingEventNotifier.java +++ b/src/java.naming/share/classes/com/sun/jndi/ldap/NamingEventNotifier.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, 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 @@ -33,6 +33,7 @@ import javax.naming.ldap.LdapName; import java.util.Vector; import com.sun.jndi.toolkit.ctx.Continuation; +import jdk.internal.misc.InnocuousThread; /** * Gathers information to generate events by using the Persistent Search @@ -86,7 +87,7 @@ final class NamingEventNotifier implements Runnable { namingListeners = new Vector<>(); namingListeners.addElement(firstListener); - worker = new Thread(this); + worker = InnocuousThread.newSystemThread("LDAP Event Notifier", this); worker.setDaemon(true); // not a user thread worker.start(); } @@ -111,6 +112,7 @@ final class NamingEventNotifier implements Runnable { * For each result, create the appropriate NamingEvent and * queue to be dispatched to listeners. */ + @Override public void run() { try { Continuation cont = new Continuation(); diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/HttpRequestBuilderImpl.java b/src/java.net.http/share/classes/jdk/internal/net/http/HttpRequestBuilderImpl.java index ef0e2b152bb..c39edf878c9 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/HttpRequestBuilderImpl.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/HttpRequestBuilderImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -145,7 +145,7 @@ public class HttpRequestBuilderImpl implements HttpRequest.Builder { @Override public HttpRequestBuilderImpl headers(String... params) { requireNonNull(params); - if (params.length == 0 || params.length % 2 != 0) { + if (params.length % 2 != 0) { throw newIAE("wrong number, %d, of parameters", params.length); } for (int i = 0; i < params.length; i += 2) { diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/quic/ConnectionTerminatorImpl.java b/src/java.net.http/share/classes/jdk/internal/net/http/quic/ConnectionTerminatorImpl.java index 5e2384dce27..3fc013b4fde 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/quic/ConnectionTerminatorImpl.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/quic/ConnectionTerminatorImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, 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 @@ -203,6 +203,9 @@ final class ConnectionTerminatorImpl implements ConnectionTerminator { // an endpoint has been established (which is OK) return; } + // close the connection ID managers; any in-flight connection ID changes should be ignored. + connection.localConnectionIdManager().close(); + connection.peerConnectionIdManager().close(); endpoint.removeConnection(this.connection); } @@ -434,6 +437,9 @@ final class ConnectionTerminatorImpl implements ConnectionTerminator { final QuicPacket packet = connection.newQuicPacket(keySpace, List.of(toSend)); final ProtectionRecord protectionRecord = ProtectionRecord.single(packet, connection::allocateDatagramForEncryption); + // close the connection ID managers; any in-flight connection ID changes should be ignored. + connection.localConnectionIdManager().close(); + connection.peerConnectionIdManager().close(); // while sending the packet containing the CONNECTION_CLOSE frame, the pushDatagram will // remap the QuicConnectionImpl in QuicEndpoint. connection.pushDatagram(protectionRecord); diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/quic/PeerConnIdManager.java b/src/java.net.http/share/classes/jdk/internal/net/http/quic/PeerConnIdManager.java index 2bc759a920a..0646026e28b 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/quic/PeerConnIdManager.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/quic/PeerConnIdManager.java @@ -65,6 +65,7 @@ final class PeerConnIdManager { private final QuicConnectionImpl connection; private final String logTag; private final boolean isClient; + private boolean closed; // when true, no more reset tokens are registered private enum State { INITIAL_PKT_NOT_RECEIVED_FROM_PEER, @@ -267,6 +268,7 @@ final class PeerConnIdManager { if (handshakeConnId == null) { throw new IllegalStateException("No handshake peer connection available"); } + if (closed) return; // recreate the conn id with the stateless token this.peerConnectionIds.put(0L, new PeerConnectionId(handshakeConnId.asReadOnlyBuffer(), statelessResetToken)); @@ -283,6 +285,10 @@ final class PeerConnIdManager { public List activeResetTokens() { lock.lock(); try { + // this method is currently only used to remove a connection from the endpoint + // after the connection is closed. + // The below assert can be removed if the method is needed elsewhere. + assert closed; // we only support one active connection ID at the time PeerConnectionId cid = peerConnectionIds.get(activeConnIdSeq); byte[] statelessResetToken = null; @@ -305,7 +311,7 @@ final class PeerConnIdManager { QuicConnectionId getPeerConnId() { lock.lock(); try { - if (activeConnIdSeq < largestReceivedRetirePriorTo) { + if (activeConnIdSeq < largestReceivedRetirePriorTo && !closed) { // stop using the old connection ID switchConnectionId(); } @@ -496,9 +502,11 @@ final class PeerConnIdManager { // connection ids. It does however store the peer-issued stateless reset token of a // peer connection id, so we let the endpoint know that the stateless reset token needs // to be forgotten since the corresponding peer connection id is being retired - final byte[] resetTokenToForget = entry.getValue().getStatelessResetToken(); - if (resetTokenToForget != null) { - this.connection.endpoint().forgetStatelessResetToken(resetTokenToForget); + if (seqNumToRetire == activeConnIdSeq) { + final byte[] resetTokenToForget = entry.getValue().getStatelessResetToken(); + if (resetTokenToForget != null) { + this.connection.endpoint().forgetStatelessResetToken(resetTokenToForget); + } } } for (Iterator iterator = gaps.iterator(); iterator.hasNext(); ) { @@ -540,4 +548,13 @@ final class PeerConnIdManager { lock.unlock(); } } + + public void close() { + lock.lock(); + try { + closed = true; + } finally { + lock.unlock(); + } + } } diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicConnectionImpl.java b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicConnectionImpl.java index 41b814a551c..b13d49ead7d 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicConnectionImpl.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicConnectionImpl.java @@ -1758,6 +1758,10 @@ public class QuicConnectionImpl extends QuicConnection implements QuicPacketRece return localConnIdManager; } + PeerConnIdManager peerConnectionIdManager() { + return peerConnIdManager; + } + /** * {@return the local connection id} */ diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicEndpoint.java b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicEndpoint.java index ef342d4cb56..3dee814e1f1 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicEndpoint.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicEndpoint.java @@ -1532,12 +1532,16 @@ public abstract sealed class QuicEndpoint implements AutoCloseable */ void removeConnection(final QuicPacketReceiver connection) { if (debug.on()) debug.log("removing connection " + connection); - // remove the connection completely - connection.connectionIds().forEach(connections::remove); - assert !connections.containsValue(connection) : connection; // remove references to this connection from the map which holds the peer issued // reset tokens dropPeerIssuedResetTokensFor(connection); + // remove the connection completely + connection.connectionIds().forEach(connections::remove); + assert !connections.containsValue(connection) : connection; + // Check that if there are no connections, there are no reset tokens either. + // This is safe because connections are added before reset tokens and removed after, + // except when we're closing the endpoint and don't bother with removing tokens. + assert peerIssuedResetTokens.isEmpty() || !connections.isEmpty() || closed : peerIssuedResetTokens; } /** @@ -1587,7 +1591,6 @@ public abstract sealed class QuicEndpoint implements AutoCloseable if (closed) return; final long idleTimeout = connection.peerPtoMs() * 3; // 3 PTO - connection.localConnectionIdManager().close(); DrainingConnection draining = new DrainingConnection(connection.connectionIds(), connection.activeResetTokens(), idleTimeout); // we can ignore stateless reset in the draining state. @@ -1626,7 +1629,6 @@ public abstract sealed class QuicEndpoint implements AutoCloseable closingDatagram.flip(); final long idleTimeout = connection.peerPtoMs() * 3; // 3 PTO - connection.localConnectionIdManager().close(); var closingConnection = new ClosingConnection(connection.connectionIds(), connection.activeResetTokens(), idleTimeout, datagram); remapPeerIssuedResetToken(closingConnection); diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/quic/TerminationCause.java b/src/java.net.http/share/classes/jdk/internal/net/http/quic/TerminationCause.java index 9e441cf7873..df8c229a000 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/quic/TerminationCause.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/quic/TerminationCause.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, 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 @@ -128,6 +128,9 @@ public abstract sealed class TerminationCause { ? new IOException("connection terminated") : new IOException(fallbackExceptionMsg); } else if (original instanceof QuicTransportException qte) { + if (qte.getCause() instanceof IOException ioe) { + return ioe; + } return new IOException(qte.getMessage()); } else if (original instanceof IOException ioe) { return ioe; diff --git a/src/jdk.accessibility/windows/classes/com/sun/java/accessibility/internal/AccessBridge.java b/src/jdk.accessibility/windows/classes/com/sun/java/accessibility/internal/AccessBridge.java index c932d0f73ff..718acf6a6b8 100644 --- a/src/jdk.accessibility/windows/classes/com/sun/java/accessibility/internal/AccessBridge.java +++ b/src/jdk.accessibility/windows/classes/com/sun/java/accessibility/internal/AccessBridge.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, 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 @@ -120,9 +120,6 @@ import com.sun.java.accessibility.util.AccessibilityEventMonitor; import com.sun.java.accessibility.util.EventQueueMonitor; import com.sun.java.accessibility.util.SwingEventMonitor; import com.sun.java.accessibility.util.Translator; -import sun.awt.AWTAccessor; -import sun.awt.AppContext; -import sun.awt.SunToolkit; /* * Note: This class has to be public. It's loaded from the VM like this: @@ -5292,7 +5289,6 @@ public final class AccessBridge { ac = a.getAccessibleContext(); } if (ac != null) { - InvocationUtils.registerAccessibleContext(ac, AppContext.getAppContext()); accessBridge.debugString("[INFO]: AccessibleContext: " + ac); String propertyName = e.getPropertyName(); @@ -5385,11 +5381,9 @@ public final class AccessBridge { if (e.getOldValue() instanceof AccessibleContext) { oldAC = (AccessibleContext) e.getOldValue(); - InvocationUtils.registerAccessibleContext(oldAC, AppContext.getAppContext()); } if (e.getNewValue() instanceof AccessibleContext) { newAC = (AccessibleContext) e.getNewValue(); - InvocationUtils.registerAccessibleContext(newAC, AppContext.getAppContext()); } accessBridge.debugString("[INFO]: - about to call propertyChildChange() old AC: " + oldAC + "new AC: " + newAC); accessBridge.propertyChildChange(e, ac, oldAC, newAC); @@ -5455,8 +5449,6 @@ public final class AccessBridge { prevAC = newAC; accessBridge.debugString("[INFO]: - about to call propertyActiveDescendentChange() AC: " + ac + " old AC: " + oldAC + "new AC: " + newAC); - InvocationUtils.registerAccessibleContext(oldAC, AppContext.getAppContext()); - InvocationUtils.registerAccessibleContext(newAC, AppContext.getAppContext()); accessBridge.propertyActiveDescendentChange(e, ac, oldAC, newAC); } @@ -5493,14 +5485,12 @@ public final class AccessBridge { // selected. The menu itself is selected. FocusEvent e = new FocusEvent(penult, FocusEvent.FOCUS_GAINED); AccessibleContext context = penult.getAccessibleContext(); - InvocationUtils.registerAccessibleContext(context, SunToolkit.targetToAppContext(penult)); accessBridge.focusGained(e, context); } else if (penult instanceof JPopupMenu) { // This is a popup with an item selected FocusEvent e = new FocusEvent(last, FocusEvent.FOCUS_GAINED); AccessibleContext focusedAC = last.getAccessibleContext(); - InvocationUtils.registerAccessibleContext(focusedAC, SunToolkit.targetToAppContext(last)); accessBridge.debugString("[INFO]: - about to call focusGained() AC: " + focusedAC); accessBridge.focusGained(e, focusedAC); } @@ -5511,7 +5501,6 @@ public final class AccessBridge { FocusEvent e = new FocusEvent(focusOwner, FocusEvent.FOCUS_GAINED); AccessibleContext focusedAC = focusOwner.getAccessibleContext(); - InvocationUtils.registerAccessibleContext(focusedAC, SunToolkit.targetToAppContext(focusOwner)); accessBridge.debugString("[INFO]: - about to call focusGained() AC: " + focusedAC); accessBridge.focusGained(e, focusedAC); } @@ -5524,7 +5513,6 @@ public final class AccessBridge { if (a != null) { accessBridge.debugString("[INFO]: - about to call focusLost() AC: " + a.getAccessibleContext()); AccessibleContext context = a.getAccessibleContext(); - InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); accessBridge.focusLost(e, context); } } @@ -5538,7 +5526,6 @@ public final class AccessBridge { Accessible a = Translator.getAccessible(e.getSource()); if (a != null) { AccessibleContext context = a.getAccessibleContext(); - InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); accessBridge.caretUpdate(e, context); } } @@ -5553,7 +5540,6 @@ public final class AccessBridge { Accessible a = Translator.getAccessible(e.getSource()); if (a != null) { AccessibleContext context = a.getAccessibleContext(); - InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); accessBridge.mouseClicked(e, context); } } @@ -5564,7 +5550,6 @@ public final class AccessBridge { Accessible a = Translator.getAccessible(e.getSource()); if (a != null) { AccessibleContext context = a.getAccessibleContext(); - InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); accessBridge.mouseEntered(e, context); } } @@ -5575,7 +5560,6 @@ public final class AccessBridge { Accessible a = Translator.getAccessible(e.getSource()); if (a != null) { AccessibleContext context = a.getAccessibleContext(); - InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); accessBridge.mouseExited(e, context); } } @@ -5586,7 +5570,6 @@ public final class AccessBridge { Accessible a = Translator.getAccessible(e.getSource()); if (a != null) { AccessibleContext context = a.getAccessibleContext(); - InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); accessBridge.mousePressed(e, context); } } @@ -5597,7 +5580,6 @@ public final class AccessBridge { Accessible a = Translator.getAccessible(e.getSource()); if (a != null) { AccessibleContext context = a.getAccessibleContext(); - InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); accessBridge.mouseReleased(e, context); } } @@ -5611,7 +5593,6 @@ public final class AccessBridge { Accessible a = Translator.getAccessible(e.getSource()); if (a != null) { AccessibleContext context = a.getAccessibleContext(); - InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); accessBridge.menuCanceled(e, context); } } @@ -5622,7 +5603,6 @@ public final class AccessBridge { Accessible a = Translator.getAccessible(e.getSource()); if (a != null) { AccessibleContext context = a.getAccessibleContext(); - InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); accessBridge.menuDeselected(e, context); } } @@ -5633,7 +5613,6 @@ public final class AccessBridge { Accessible a = Translator.getAccessible(e.getSource()); if (a != null) { AccessibleContext context = a.getAccessibleContext(); - InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); accessBridge.menuSelected(e, context); } } @@ -5644,7 +5623,6 @@ public final class AccessBridge { Accessible a = Translator.getAccessible(e.getSource()); if (a != null) { AccessibleContext context = a.getAccessibleContext(); - InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); accessBridge.popupMenuCanceled(e, context); } } @@ -5655,7 +5633,6 @@ public final class AccessBridge { Accessible a = Translator.getAccessible(e.getSource()); if (a != null) { AccessibleContext context = a.getAccessibleContext(); - InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); accessBridge.popupMenuWillBecomeInvisible(e, context); } } @@ -5666,7 +5643,6 @@ public final class AccessBridge { Accessible a = Translator.getAccessible(e.getSource()); if (a != null) { AccessibleContext context = a.getAccessibleContext(); - InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); accessBridge.popupMenuWillBecomeVisible(e, context); } } @@ -7227,8 +7203,7 @@ public final class AccessBridge { private static class InvocationUtils { /** - * Invokes a {@code Callable} in the {@code AppContext} of the given {@code Accessible} - * and waits for it to finish blocking the caller thread. + * Invokes a {@code Callable} and waits for it to finish blocking the caller thread. * * @param callable the {@code Callable} to invoke * @param accessibleTable the {@code AccessibleExtendedTable} which would be used to find the right context @@ -7246,8 +7221,7 @@ public final class AccessBridge { } /** - * Invokes a {@code Callable} in the {@code AppContext} of the given {@code Accessible} - * and waits for it to finish blocking the caller thread. + * Invokes a {@code Callable} and waits for it to finish blocking the caller thread. * * @param callable the {@code Callable} to invoke * @param accessible the {@code Accessible} which would be used to find the right context @@ -7269,8 +7243,7 @@ public final class AccessBridge { } /** - * Invokes a {@code Callable} in the {@code AppContext} of the given {@code Component} - * and waits for it to finish blocking the caller thread. + * Invokes a {@code Callable} and waits for it to finish blocking the caller thread. * * @param callable the {@code Callable} to invoke * @param component the {@code Component} which would be used to find the right context @@ -7281,12 +7254,11 @@ public final class AccessBridge { */ public static T invokeAndWait(final Callable callable, final Component component) { - return invokeAndWait(callable, SunToolkit.targetToAppContext(component)); + return invokeAndWait(callable); } /** - * Invokes a {@code Callable} in the {@code AppContext} mapped to the given {@code AccessibleContext} - * and waits for it to finish blocking the caller thread. + * Invokes a {@code Callable} and waits for it to finish blocking the caller thread. * * @param callable the {@code Callable} to invoke * @param accessibleContext the {@code AccessibleContext} which would be used to determine the right @@ -7297,45 +7269,26 @@ public final class AccessBridge { */ public static T invokeAndWait(final Callable callable, final AccessibleContext accessibleContext) { - AppContext targetContext = AWTAccessor.getAccessibleContextAccessor() - .getAppContext(accessibleContext); - if (targetContext != null) { - return invokeAndWait(callable, targetContext); - } else { - // Normally this should not happen, unmapped context provided and - // the target AppContext is unknown. - - // Try to recover in case the context is a translator. - if (accessibleContext instanceof Translator) { - Object source = ((Translator)accessibleContext).getSource(); - if (source instanceof Component) { - return invokeAndWait(callable, (Component)source); - } - } - } - throw new RuntimeException("Unmapped AccessibleContext used to dispatch event: " + accessibleContext); + return invokeAndWait(callable); } - private static T invokeAndWait(final Callable callable, - final AppContext targetAppContext) { + private static T invokeAndWait(final Callable callable) { final CallableWrapper wrapper = new CallableWrapper(callable); try { - invokeAndWait(wrapper, targetAppContext); + invokeAndWait(wrapper); T result = wrapper.getResult(); - updateAppContextMap(result, targetAppContext); return result; } catch (final Exception e) { throw new RuntimeException(e); } } - private static void invokeAndWait(final Runnable runnable, - final AppContext appContext) + private static void invokeAndWait(final Runnable runnable) throws InterruptedException, InvocationTargetException { - EventQueue eq = SunToolkit.getSystemEventQueueImplPP(appContext); Object lock = new Object(); Toolkit source = Toolkit.getDefaultToolkit(); + EventQueue eq = source.getSystemEventQueue(); InvocationEvent event = new InvocationEvent(source, runnable, lock, true); synchronized (lock) { @@ -7349,26 +7302,6 @@ public final class AccessBridge { } } - /** - * Maps the {@code AccessibleContext} to the {@code AppContext} which should be used - * to dispatch events related to the {@code AccessibleContext} - * @param accessibleContext the {@code AccessibleContext} for the mapping - * @param targetContext the {@code AppContext} for the mapping - */ - public static void registerAccessibleContext(final AccessibleContext accessibleContext, - final AppContext targetContext) { - if (accessibleContext != null) { - AWTAccessor.getAccessibleContextAccessor().setAppContext(accessibleContext, targetContext); - } - } - - private static void updateAppContextMap(final T accessibleContext, - final AppContext targetContext) { - if (accessibleContext instanceof AccessibleContext) { - registerAccessibleContext((AccessibleContext)accessibleContext, targetContext); - } - } - private static class CallableWrapper implements Runnable { private final Callable callable; private volatile T object; diff --git a/src/jdk.compiler/share/classes/com/sun/source/util/DocSourcePositions.java b/src/jdk.compiler/share/classes/com/sun/source/util/DocSourcePositions.java index 520943c464d..4eb6d12fd38 100644 --- a/src/jdk.compiler/share/classes/com/sun/source/util/DocSourcePositions.java +++ b/src/jdk.compiler/share/classes/com/sun/source/util/DocSourcePositions.java @@ -28,6 +28,7 @@ package com.sun.source.util; import com.sun.source.doctree.DocCommentTree; import com.sun.source.doctree.DocTree; import com.sun.source.tree.CompilationUnitTree; +import com.sun.source.tree.Tree; /** * Provides methods to obtain the position of a DocTree within a javadoc comment. @@ -59,8 +60,32 @@ public interface DocSourcePositions extends SourcePositions { * position is being sought * @param tree tree for which a position is sought * @return the start position of tree + * @deprecated use {@link #getStartPosition(DocCommentTree, DocTree)} instead */ - long getStartPosition(CompilationUnitTree file, DocCommentTree comment, DocTree tree); + @Deprecated(since = "27", forRemoval = true) + default long getStartPosition(CompilationUnitTree file, DocCommentTree comment, DocTree tree) { + return getStartPosition(comment, tree); + } + + /** + * {@return the starting position of the given {@link Tree}. If the starting position is not available, returns + * {@link javax.tools.Diagnostic#NOPOS}} + * + *

The given tree should be under the given comment tree. The returned position must be at the start of the + * yield of this tree, that is for any sub-tree of this tree, the following must hold: + * + *

+ * {@code getStartPosition(comment, tree) <= getStartPosition(comment, subtree)} or
+ * {@code getStartPosition(comment, tree) == NOPOS} or
+ * {@code getStartPosition(comment, subtree) == NOPOS} + *

+ * + * @param comment the comment tree that encloses the tree for which the + * position is being sought + * @param tree tree for which a position is sought + * @since 27 + */ + long getStartPosition(DocCommentTree comment, DocTree tree); /** * Returns the ending position of the tree within the comment within the file. If tree is not found within @@ -91,7 +116,39 @@ public interface DocSourcePositions extends SourcePositions { * position is being sought * @param tree tree for which a position is sought * @return the end position of tree + * @deprecated use {@link #getEndPosition(DocCommentTree, DocTree)} instead */ - long getEndPosition(CompilationUnitTree file, DocCommentTree comment, DocTree tree); + @Deprecated(since = "27", forRemoval = true) + default long getEndPosition(CompilationUnitTree file, DocCommentTree comment, DocTree tree) { + return getEndPosition(comment, tree); + } + + /** + * {@return the ending position of the given {@link Tree}. If the ending position is not available, returns + * {@link javax.tools.Diagnostic#NOPOS}} + * + *

The given tree should be under the given comment tree. The returned position must be at the end of the yield + * of this tree, that is for any sub-tree of this tree, the following must hold: + * + *

+ * {@code getEndPosition(comment, tree) >= getEndPosition(comment, subtree)} or
+ * {@code getEndPosition(comment, tree) == NOPOS} or
+ * {@code getEndPosition(comment, subtree) == NOPOS} + *

+ * + * In addition, the following must hold: + * + *

+ * {@code getStartPosition(comment, tree) <= getEndPosition(comment, tree)} or
+ * {@code getStartPosition(comment, tree) == NOPOS} or
+ * {@code getEndPosition(comment, tree) == NOPOS} + *

+ * + * @param comment the comment tree that encloses the tree for which the + * position is being sought + * @param tree tree for which a position is sought + * @since 27 + */ + long getEndPosition(DocCommentTree comment, DocTree tree); } diff --git a/src/jdk.compiler/share/classes/com/sun/source/util/SourcePositions.java b/src/jdk.compiler/share/classes/com/sun/source/util/SourcePositions.java index b6112fd32e6..3ff6fafe58b 100644 --- a/src/jdk.compiler/share/classes/com/sun/source/util/SourcePositions.java +++ b/src/jdk.compiler/share/classes/com/sun/source/util/SourcePositions.java @@ -53,8 +53,30 @@ public interface SourcePositions { * @param file CompilationUnit in which to find tree * @param tree tree for which a position is sought * @return the start position of tree + * @deprecated use {@link #getStartPosition(Tree)} instead */ - long getStartPosition(CompilationUnitTree file, Tree tree); + @Deprecated(since = "27", forRemoval = true) + default long getStartPosition(CompilationUnitTree file, Tree tree) { + return getStartPosition(tree); + } + + /** + * {@return the starting position of the given {@link Tree}, or if the starting position is not available, returns + * {@link javax.tools.Diagnostic#NOPOS}} + * + *

The returned position must be at the start of the yield of this tree, that is for any sub-tree of this tree, + * the following must hold: + * + *

+ * {@code getStartPosition(tree) <= getStartPosition(subtree)} or
+ * {@code getStartPosition(tree) == NOPOS} or
+ * {@code getStartPosition(subtree) == NOPOS} + *

+ * + * @param tree tree for which a position is sought + * @since 27 + */ + long getStartPosition(Tree tree); /** * Returns the ending position of tree within file. If tree is not found within @@ -80,7 +102,36 @@ public interface SourcePositions { * @param file CompilationUnit in which to find tree * @param tree tree for which a position is sought * @return the end position of tree + * @deprecated use {@link #getEndPosition(Tree)} instead */ - long getEndPosition(CompilationUnitTree file, Tree tree); + @Deprecated(since = "27", forRemoval = true) + default long getEndPosition(CompilationUnitTree file, Tree tree) { + return getEndPosition(tree); + } + /** + * {@return the ending position of the given {@link Tree}. If the ending position is not available, + * returns {@link javax.tools.Diagnostic#NOPOS}} + * + *

The returned position must be at the end of the yield of this tree, that is for any sub-tree of this tree, + * the following must hold: + * + *

+ * {@code getEndPosition(tree) >= getEndPosition(subtree)} or
+ * {@code getEndPosition(tree) == NOPOS} or
+ * {@code getEndPosition(subtree) == NOPOS} + *

+ * + * In addition, the following must hold: + * + *

+ * {@code getStartPosition(tree) <= getEndPosition(tree)} or
+ * {@code getStartPosition(tree) == NOPOS} or
+ * {@code getEndPosition(tree) == NOPOS} + *

+ * + * @param tree tree for which a position is sought + * @since 27 + */ + long getEndPosition(Tree tree); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java index ecd7f5b101a..41dd904bc8a 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java @@ -233,24 +233,24 @@ public class JavacTrees extends DocTrees { public DocSourcePositions getSourcePositions() { return new DocSourcePositions() { @Override @DefinedBy(Api.COMPILER_TREE) - public long getStartPosition(CompilationUnitTree file, Tree tree) { + public long getStartPosition(Tree tree) { return TreeInfo.getStartPos((JCTree) tree); } @Override @DefinedBy(Api.COMPILER_TREE) - public long getEndPosition(CompilationUnitTree file, Tree tree) { + public long getEndPosition(Tree tree) { return TreeInfo.getEndPos((JCTree) tree); } @Override @DefinedBy(Api.COMPILER_TREE) - public long getStartPosition(CompilationUnitTree file, DocCommentTree comment, DocTree tree) { + public long getStartPosition(DocCommentTree comment, DocTree tree) { DCDocComment dcComment = (DCDocComment) comment; DCTree dcTree = (DCTree) tree; return dcComment.getSourcePosition(dcTree.getStartPosition()); } @Override @DefinedBy(Api.COMPILER_TREE) - public long getEndPosition(CompilationUnitTree file, DocCommentTree comment, DocTree tree) { + public long getEndPosition(DocCommentTree comment, DocTree tree) { DCDocComment dcComment = (DCDocComment) comment; DCTree dcTree = (DCTree) tree; return dcComment.getSourcePosition(dcTree.getEndPosition()); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java index 773c573c201..776fd544bfb 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, 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 @@ -37,11 +37,16 @@ import java.util.Set; import java.util.stream.Stream; import com.sun.tools.javac.main.Option; +import com.sun.tools.javac.util.Assert; import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.JCDiagnostic; import com.sun.tools.javac.util.Log; import com.sun.tools.javac.util.Names; import com.sun.tools.javac.util.Options; +import static com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag.DEFAULT_ENABLED; +import static com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag.DEPRECATION_SENSITIVE; + /** * A class for handling -Xlint suboptions and @SuppressWarnings. * @@ -76,35 +81,20 @@ public class Lint { */ public Lint augment(Symbol sym) { EnumSet suppressions = suppressionsFrom(sym); - if (!suppressions.isEmpty()) { + boolean symWithinDeprecated = withinDeprecated || isDeprecatedDeclaration(sym); + if (!suppressions.isEmpty() || symWithinDeprecated != withinDeprecated) { Lint lint = new Lint(this); lint.values.removeAll(suppressions); lint.suppressedValues.addAll(suppressions); + lint.withinDeprecated = symWithinDeprecated; return lint; } return this; } - /** - * Returns a new Lint that has the given LintCategorys enabled. - * @param lc one or more categories to be enabled - */ - public Lint enable(LintCategory... lc) { - Lint l = new Lint(this); - l.values.addAll(Arrays.asList(lc)); - l.suppressedValues.removeAll(Arrays.asList(lc)); - return l; - } - - /** - * Returns a new Lint that has the given LintCategorys suppressed. - * @param lc one or more categories to be suppressed - */ - public Lint suppress(LintCategory... lc) { - Lint l = new Lint(this); - l.values.removeAll(Arrays.asList(lc)); - l.suppressedValues.addAll(Arrays.asList(lc)); - return l; + // Does sym's declaration have a (non-useless) @Deprecated annotation? + public static boolean isDeprecatedDeclaration(Symbol sym) { + return sym.isDeprecated() && sym.isDeprecatableViaAnnotation(); } private final Context context; @@ -115,9 +105,12 @@ public class Lint { private Symtab syms; private Names names; - // Invariant: it's never the case that a category is in both "values" and "suppressedValues" + // Invariants: + // - It's never the case that a category is in both "values" and "suppressedValues" + // - All categories in "suppressedValues" have annotationSuppression = true private EnumSet values; private EnumSet suppressedValues; + private boolean withinDeprecated; private static final Map map = new LinkedHashMap<>(40); @@ -129,7 +122,7 @@ public class Lint { log = Log.instance(context); } - // Instantiate a non-root ("symbol scoped") instance + // Copy constructor - used to instantiate a non-root ("symbol scoped") instances protected Lint(Lint other) { other.initializeRootIfNeeded(); this.context = other.context; @@ -139,6 +132,7 @@ public class Lint { this.names = other.names; this.values = other.values.clone(); this.suppressedValues = other.suppressedValues.clone(); + this.withinDeprecated = other.withinDeprecated; } // Process command line options on demand to allow use of root Lint early during startup @@ -169,7 +163,7 @@ public class Lint { @Override public String toString() { initializeRootIfNeeded(); - return "Lint:[enable" + values + ",suppress" + suppressedValues + "]"; + return "Lint:[enable" + values + ",suppress" + suppressedValues + ",deprecated=" + withinDeprecated + "]"; } /** @@ -443,6 +437,34 @@ public class Lint { public final boolean enabledByDefault; } + /** + * Determine if the given diagnostic should be emitted given the state of this instance. + */ + public boolean shouldEmit(JCDiagnostic diag) { + + // Check category + LintCategory category = diag.getLintCategory(); + if (category == null) + return true; + + // Certain warnings within @Deprecated declarations are automatically suppressed (JLS 9.6.4.6) + if (withinDeprecated && diag.isFlagSet(DEPRECATION_SENSITIVE)) { + Assert.check(diag.isFlagSet(DEFAULT_ENABLED) && category.annotationSuppression); + return false; + } + + // If the warning is not enabled by default, then emit only when its lint category is explicitly enabled + if (!diag.isFlagSet(DEFAULT_ENABLED)) + return isEnabled(category); + + // If the lint category doesn't support @SuppressWarnings, then we just check the -Xlint:category flag + if (!category.annotationSuppression) + return !options.isDisabled(Option.XLINT, category); + + // Check whether the lint category is currently suppressed + return !isSuppressed(category); + } + /** * Checks if a warning category is enabled. A warning category may be enabled * on the command line, or by default, and can be temporarily disabled with @@ -454,10 +476,11 @@ public class Lint { } /** - * Checks is a warning category has been specifically suppressed, by means - * of the SuppressWarnings annotation, or, in the case of the deprecated - * category, whether it has been implicitly suppressed by virtue of the - * current entity being itself deprecated. + * Check if a warning category has been specifically suppressed by means of @SuppressWarnings. + * + *

+ * Always returns false for categories that are not suppressible by the annotation, even + * if they (uselessly) happen to appear in one. */ public boolean isSuppressed(LintCategory lc) { initializeRootIfNeeded(); @@ -468,17 +491,14 @@ public class Lint { * Obtain the set of recognized lint warning categories suppressed at the given symbol's declaration. * *

- * This set can be non-empty only if the symbol is annotated with either - * @SuppressWarnings or @Deprecated. + * This set can be non-empty only if the symbol is annotated with @SuppressWarnings, and only categories + * for which {@code annotationSuppression} is true are included. * * @param symbol symbol corresponding to a possibly-annotated declaration * @return new warning suppressions applied to sym */ public EnumSet suppressionsFrom(Symbol symbol) { - EnumSet suppressions = suppressionsFrom(symbol.getDeclarationAttributes().stream()); - if (symbol.isDeprecated() && symbol.isDeprecatableViaAnnotation()) - suppressions.add(LintCategory.DEPRECATION); - return suppressions; + return suppressionsFrom(symbol.getDeclarationAttributes().stream()); } // Find the @SuppressWarnings annotation in the given stream and extract the recognized suppressions diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java index 1c93c37698a..7a8e6c6f4f1 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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,6 +34,7 @@ import com.sun.tools.javac.resources.CompilerProperties.LintWarnings; import com.sun.tools.javac.resources.CompilerProperties.Warnings; import com.sun.tools.javac.util.Assert; import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; import com.sun.tools.javac.util.JCDiagnostic.Error; import com.sun.tools.javac.util.JCDiagnostic.LintWarning; @@ -150,24 +151,26 @@ public class Preview { /** * Report usage of a preview feature. Usages reported through this method will affect the * set of sourcefiles with dependencies on preview features. + * @param flag a flag to set on the diagnostic * @param pos the position at which the preview feature was used. * @param feature the preview feature used. */ - public void warnPreview(int pos, Feature feature) { - warnPreview(new SimpleDiagnosticPosition(pos), feature); + public void warnPreview(DiagnosticFlag flag, int pos, Feature feature) { + warnPreview(flag, new SimpleDiagnosticPosition(pos), feature); } /** * Report usage of a preview feature. Usages reported through this method will affect the * set of sourcefiles with dependencies on preview features. + * @param flag a flag to set on the diagnostic * @param pos the position at which the preview feature was used. * @param feature the preview feature used. */ - public void warnPreview(DiagnosticPosition pos, Feature feature) { + public void warnPreview(DiagnosticFlag flag, DiagnosticPosition pos, Feature feature) { Assert.check(isEnabled()); Assert.check(isPreview(feature)); markUsesPreview(pos); - log.warning(pos, + log.warning(flag, pos, feature.isPlural() ? LintWarnings.PreviewFeatureUsePlural(feature.nameFragment()) : LintWarnings.PreviewFeatureUse(feature.nameFragment())); @@ -263,7 +266,7 @@ public class Preview { log.error(pos, feature.error(source.name)); } if (isEnabled() && isPreview(feature)) { - warnPreview(pos, feature); + warnPreview(null, pos, feature); } } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Printer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Printer.java index d9781f19c5d..acd8a35a894 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Printer.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Printer.java @@ -53,6 +53,7 @@ public abstract class Printer implements Type.Visitor, Symbol.Vi List seenCaptured = List.nil(); static final int PRIME = 997; // largest prime less than 1000 + private boolean printingMethodArgs; protected Printer() { } @@ -195,6 +196,9 @@ public abstract class Printer implements Type.Visitor, Symbol.Vi } private String printAnnotations(Type t, boolean prefix) { + if (printingMethodArgs) { + return ""; + } StringBuilder sb = new StringBuilder(); List annos = t.getAnnotationMirrors(); if (!annos.isEmpty()) { @@ -337,27 +341,28 @@ public abstract class Printer implements Type.Visitor, Symbol.Vi * @return localized string representation */ protected String printMethodArgs(List args, boolean varArgs, Locale locale) { - if (!varArgs) { - return visitTypes(args, locale); - } else { - StringBuilder buf = new StringBuilder(); - while (args.tail.nonEmpty()) { - buf.append(visit(args.head, locale)); - args = args.tail; - buf.append(','); - } - if (args.head.hasTag(TypeTag.ARRAY)) { - buf.append(visit(((ArrayType) args.head).elemtype, locale)); - if (args.head.getAnnotationMirrors().nonEmpty()) { - buf.append(' '); - buf.append(args.head.getAnnotationMirrors()); - buf.append(' '); - } - buf.append("..."); + boolean prev = printingMethodArgs; + printingMethodArgs = true; + try { + if (!varArgs) { + return visitTypes(args, locale); } else { - buf.append(visit(args.head, locale)); + StringBuilder buf = new StringBuilder(); + while (args.tail.nonEmpty()) { + buf.append(visit(args.head, locale)); + args = args.tail; + buf.append(','); + } + if (args.head.hasTag(TypeTag.ARRAY)) { + buf.append(visit(((ArrayType) args.head).elemtype, locale)); + buf.append("..."); + } else { + buf.append(visit(args.head, locale)); + } + return buf.toString(); } - return buf.toString(); + } finally { + printingMethodArgs = prev; } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotations.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotations.java index 8eba79c7480..452d15ed219 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotations.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotations.java @@ -154,6 +154,14 @@ public class TypeAnnotations { } else { pos.push(env.enclMethod); } + Env env1 = env; + while (env1 != null && !env1.tree.hasTag(Tag.CLASSDEF)) { + if (env1.tree instanceof JCLambda l) { + pos.currentLambda = l; + break; + } + env1 = env1.next; + } pos.scan(tree); } finally { log.useSource(oldSource); @@ -472,11 +480,12 @@ public class TypeAnnotations { Assert.check(tc.position == pos); } - if (type.hasTag(TypeTag.ARRAY)) - return rewriteArrayType(typetree, (ArrayType)type, annotations, onlyTypeAnnotations, pos); + Type ret; - if (type.hasTag(TypeTag.TYPEVAR)) { - return type.annotatedType(onlyTypeAnnotations); + if (type.hasTag(TypeTag.ARRAY)) { + ret = rewriteArrayType(typetree, (ArrayType)type, annotations, onlyTypeAnnotations, pos); + } else if (type.hasTag(TypeTag.TYPEVAR)) { + ret = type.annotatedType(onlyTypeAnnotations); } else if (type.getKind() == TypeKind.UNION) { // There is a TypeKind, but no TypeTag. UnionClassType ut = (UnionClassType) type; @@ -487,7 +496,7 @@ public class TypeAnnotations { ListBuffer alternatives = new ListBuffer<>(); alternatives.add(res); alternatives.addAll(ut.alternatives_field.tail); - return new UnionClassType((ClassType) ut.getLub(), alternatives.toList()); + ret = new UnionClassType((ClassType) ut.getLub(), alternatives.toList()); } else { Type enclTy = type; Element enclEl = type.asElement(); @@ -567,10 +576,10 @@ public class TypeAnnotations { pos.location = pos.location.appendList(depth.toList()); } - Type ret = typeWithAnnotations(type, enclTy, annotations); - typetree.type = ret; - return ret; + ret = typeWithAnnotations(type, enclTy, annotations); } + typetree.type = ret; + return ret; } /** diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index 444530c7266..89ae68e85ba 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -5270,11 +5270,15 @@ public class Attr extends JCTree.Visitor { public void visitAnnotatedType(JCAnnotatedType tree) { attribAnnotationTypes(tree.annotations, env); - Type underlyingType = attribType(tree.underlyingType, env); - Type annotatedType = underlyingType.preannotatedType(); + Type underlyingType = attribTree(tree.underlyingType, env, resultInfo); + if (underlyingType.getTag() == PACKAGE) { + result = tree.type = underlyingType; + } else { + Type annotatedType = underlyingType.preannotatedType(); - annotate.annotateTypeSecondStage(tree, tree.annotations, annotatedType); - result = tree.type = annotatedType; + annotate.annotateTypeSecondStage(tree, tree.annotations, annotatedType); + result = tree.type = annotatedType; + } } public void visitErroneous(JCErroneous tree) { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java index 017d740dc0a..08ba0442781 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java @@ -1367,9 +1367,7 @@ public class ClassReader { else self.fullname = ClassSymbol.formFullName(self.name, self.owner); - if (m != null) { - ((ClassType)sym.type).setEnclosingType(m.type); - } else if ((self.flags_field & STATIC) == 0) { + if ((self.flags_field & STATIC) == 0 && (m == null || (m.flags_field & STATIC) == 0)) { ((ClassType)sym.type).setEnclosingType(c.type); } else { ((ClassType)sym.type).setEnclosingType(Type.noType); @@ -2687,6 +2685,7 @@ public class ClassReader { // won't pass the "hasOuterInstance" check above, but those that don't have an // enclosing method (i.e. from initializers) will pass that check. boolean local = forceLocal = + currentOwner.owner.kind != TYP || !currentOwner.owner.members().includes(currentOwner, LookupKind.NON_RECURSIVE); if (!currentOwner.name.isEmpty() && !local) type = new MethodType(adjustMethodParams(flags, type.getParameterTypes()), @@ -3038,7 +3037,9 @@ public class ClassReader { * `typevars'. */ protected void enterTypevars(Symbol sym, Type t) { - if (t.getEnclosingType() != null) { + if (sym.owner.kind == MTH) { + enterTypevars(sym.owner, sym.owner.type); + } else if (t.getEnclosingType() != null) { if (!t.getEnclosingType().hasTag(TypeTag.NONE)) { enterTypevars(sym.owner, t.getEnclosingType()); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java index babe372e7dc..d8b5b1ddd6b 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, 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 @@ -176,7 +176,7 @@ public class JavaTokenizer extends UnicodeReader { lexError(pos, feature.error(source.name)); } else if (preview.isPreview(feature)) { //use of preview feature, warn - preview.warnPreview(pos, feature); + preview.warnPreview(DiagnosticFlag.SYNTAX, pos, feature); } } @@ -1000,7 +1000,7 @@ public class JavaTokenizer extends UnicodeReader { scanIdent(); } else if (digit(pos, 10) >= 0) { scanNumber(pos, 10); - } else if (is((char)EOI) || !isAvailable()) { + } else if (is((char)EOI) && position() + 1 == length() || !isAvailable()) { tk = TokenKind.EOF; pos = position(); } else { @@ -1040,10 +1040,10 @@ public class JavaTokenizer extends UnicodeReader { // Verify that the incidental indentation is consistent. Set checks = TextBlockSupport.checkWhitespace(string); if (checks.contains(TextBlockSupport.WhitespaceChecks.INCONSISTENT)) { - log.warning(pos, LintWarnings.InconsistentWhiteSpaceIndentation); + log.warning(DiagnosticFlag.SYNTAX, pos, LintWarnings.InconsistentWhiteSpaceIndentation); } if (checks.contains(TextBlockSupport.WhitespaceChecks.TRAILING)) { - log.warning(pos, LintWarnings.TrailingWhiteSpaceWillBeRemoved); + log.warning(DiagnosticFlag.SYNTAX, pos, LintWarnings.TrailingWhiteSpaceWillBeRemoved); } // Remove incidental indentation. try { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java index df5da5cb954..b4dfb04766c 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java @@ -772,7 +772,7 @@ public class JavacParser implements Parser { } } else if (token.kind == UNDERSCORE) { if (Feature.UNDERSCORE_IDENTIFIER.allowedInSource(source)) { - log.warning(token.pos, Warnings.UnderscoreAsIdentifier); + log.warning(DiagnosticFlag.SYNTAX, token.pos, Warnings.UnderscoreAsIdentifier); } else if (asVariable) { checkSourceLevel(Feature.UNNAMED_VARIABLES); if (peekToken(LBRACKET)) { @@ -2339,7 +2339,7 @@ public class JavacParser implements Parser { if (allowYieldStatement) { return true; } else { - log.warning(pos, Warnings.InvalidYield); + log.warning(DiagnosticFlag.SYNTAX, pos, Warnings.InvalidYield); } } return false; @@ -3858,35 +3858,35 @@ public class JavacParser implements Parser { if (Feature.LOCAL_VARIABLE_TYPE_INFERENCE.allowedInSource(source)) { return Source.JDK10; } else if (shouldWarn) { - log.warning(pos, Warnings.RestrictedTypeNotAllowed(name, Source.JDK10)); + log.warning(DiagnosticFlag.SYNTAX, pos, Warnings.RestrictedTypeNotAllowed(name, Source.JDK10)); } } if (name == names.yield) { if (allowYieldStatement) { return Source.JDK14; } else if (shouldWarn) { - log.warning(pos, Warnings.RestrictedTypeNotAllowed(name, Source.JDK14)); + log.warning(DiagnosticFlag.SYNTAX, pos, Warnings.RestrictedTypeNotAllowed(name, Source.JDK14)); } } if (name == names.record) { if (allowRecords) { return Source.JDK14; } else if (shouldWarn) { - log.warning(pos, Warnings.RestrictedTypeNotAllowedPreview(name, Source.JDK14)); + log.warning(DiagnosticFlag.SYNTAX, pos, Warnings.RestrictedTypeNotAllowedPreview(name, Source.JDK14)); } } if (name == names.sealed) { if (allowSealedTypes) { return Source.JDK15; } else if (shouldWarn) { - log.warning(pos, Warnings.RestrictedTypeNotAllowedPreview(name, Source.JDK15)); + log.warning(DiagnosticFlag.SYNTAX, pos, Warnings.RestrictedTypeNotAllowedPreview(name, Source.JDK15)); } } if (name == names.permits) { if (allowSealedTypes) { return Source.JDK15; } else if (shouldWarn) { - log.warning(pos, Warnings.RestrictedTypeNotAllowedPreview(name, Source.JDK15)); + log.warning(DiagnosticFlag.SYNTAX, pos, Warnings.RestrictedTypeNotAllowedPreview(name, Source.JDK15)); } } return null; @@ -4057,7 +4057,7 @@ public class JavacParser implements Parser { if (source.compareTo(Source.JDK21) >= 0) reportSyntaxError(semiList.first().pos, Errors.ExtraneousSemicolon); else - log.warning(semiList.first().pos, Warnings.ExtraneousSemicolon); + log.warning(DiagnosticFlag.SYNTAX, semiList.first().pos, Warnings.ExtraneousSemicolon); } seenImport = true; defs.append(importDeclaration()); @@ -4074,7 +4074,7 @@ public class JavacParser implements Parser { if (source.compareTo(Source.JDK21) >= 0) reportSyntaxError(semiList.first().pos, Errors.ExtraneousSemicolon); else - log.warning(semiList.first().pos, Warnings.ExtraneousSemicolon); + log.warning(DiagnosticFlag.SYNTAX, semiList.first().pos, Warnings.ExtraneousSemicolon); } ModuleKind kind = ModuleKind.STRONG; if (token.name() == names.open) { @@ -5616,7 +5616,7 @@ public class JavacParser implements Parser { log.error(pos, feature.error(source.name)); } else if (preview.isPreview(feature)) { //use of preview feature, warn - preview.warnPreview(pos, feature); + preview.warnPreview(DiagnosticFlag.SYNTAX, pos, feature); } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/TextBlockSupport.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/TextBlockSupport.java index d099ceadba0..5112849d45f 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/TextBlockSupport.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/TextBlockSupport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -56,8 +56,8 @@ class TextBlockSupport { // No need to check indentation if opting out (last line is empty.) char lastChar = string.charAt(string.length() - 1); boolean optOut = lastChar == '\n' || lastChar == '\r'; - // Split string based at line terminators. - String[] lines = string.split("\\R"); + // Split string using JLS text block line terminators: CRLF, CR, or LF. + String[] lines = string.split("\\r\\n|\\r|\\n"); int length = lines.length; // Extract last line. String lastLine = length == 0 ? "" : lines[length - 1]; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java index 74d082d4b64..11fa3a5aebf 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, 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 @@ -1104,7 +1104,7 @@ public class JavacProcessingEnvironment implements ProcessingEnvironment, Closea return true; return deferredDiagnosticHandler.getDiagnostics().stream() - .anyMatch(d -> (d.getKind() == Diagnostic.Kind.WARNING && werror) || + .anyMatch(d -> (d.getKind() == Diagnostic.Kind.WARNING && werror && ACCEPT_NON_RECOVERABLE_LINTS.test(d)) || (d.getKind() == Diagnostic.Kind.ERROR && (fatalErrors || !d.isFlagSet(RECOVERABLE)))); } @@ -1195,12 +1195,20 @@ public class JavacProcessingEnvironment implements ProcessingEnvironment, Closea } void showDiagnostics(boolean showAll) { - deferredDiagnosticHandler.reportDeferredDiagnostics(showAll ? ACCEPT_ALL - : ACCEPT_NON_RECOVERABLE); + deferredDiagnosticHandler.reportDeferredDiagnostics( + ACCEPT_NON_RECOVERABLE_LINTS.and(showAll ? ACCEPT_ALL + : ACCEPT_NON_RECOVERABLE)); log.popDiagnosticHandler(deferredDiagnosticHandler); compiler.setDeferredDiagnosticHandler(null); } //where: + private final Predicate ACCEPT_NON_RECOVERABLE_LINTS = + d -> !Optional.of(d) + .filter(diag -> !diag.isFlagSet(SYNTAX)) + .map(JCDiagnostic::getLintCategory) + .map(lc -> lc.annotationSuppression || + lc == Lint.LintCategory.INCUBATING) + .orElse(false); private final Predicate ACCEPT_NON_RECOVERABLE = d -> d.getKind() != JCDiagnostic.Kind.ERROR || !d.isFlagSet(DiagnosticFlag.RECOVERABLE) || diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties index 58a5333ce4c..a2bfb9c7201 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties @@ -1960,7 +1960,7 @@ compiler.warn.incubating.modules=\ # 0: symbol, 1: symbol # lint: deprecation -# flags: aggregate, mandatory, default-enabled +# flags: aggregate, mandatory, default-enabled, deprecation-sensitive compiler.warn.has.been.deprecated=\ {0} in {1} has been deprecated diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/AbstractLog.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/AbstractLog.java index ce3e56f2f3f..b8b2a3af254 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/AbstractLog.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/AbstractLog.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, 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 @@ -192,6 +192,16 @@ public abstract class AbstractLog { report(diags.warning(null, source, wrap(pos), warningKey)); } + /** Report a warning, unless suppressed by the -nowarn option or the + * maximum number of warnings has been reached. + * @param flag A flag to set on the diagnostic + * @param pos The source position at which to report the warning. + * @param warningKey The key for the localized warning message. + */ + public void warning(DiagnosticFlag flag, int pos, Warning warningKey) { + report(diags.warning(flag, source, wrap(pos), warningKey)); + } + /** Provide a non-fatal notification, unless suppressed by the -nowarn option. * @param noteKey The key for the localized notification message. */ diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/JCDiagnostic.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/JCDiagnostic.java index 328183c0cb3..aed42f7b2b2 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/JCDiagnostic.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/JCDiagnostic.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -436,6 +436,10 @@ public class JCDiagnostic implements Diagnostic { * is not explicitly enabled, as long as it is not explicitly suppressed. */ DEFAULT_ENABLED, + /** Flag for warnings that are automatically suppressed when they occur inside + * a declaration that is itself annotated as @Deprecated. See JLS 9.6.4.6. + */ + DEPRECATION_SENSITIVE, /** Flags mandatory warnings that should pass through a mandatory warning aggregator. */ AGGREGATE, diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java index b061d2283a0..99f71e87df1 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, 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 @@ -174,16 +174,8 @@ public class Log extends AbstractLog { } // Apply the lint configuration (if any) and discard the warning if it gets filtered out - if (lint != null) { - LintCategory category = diag.getLintCategory(); - boolean emit = !diag.isFlagSet(DEFAULT_ENABLED) ? // is the warning not enabled by default? - lint.isEnabled(category) : // then emit if the category is enabled - category.annotationSuppression ? // else emit if the category is not suppressed, where - !lint.isSuppressed(category) : // ...suppression happens via @SuppressWarnings - !options.isDisabled(Option.XLINT, category); // ...suppression happens via -Xlint:-category - if (!emit) - return; - } + if (lint != null && !lint.shouldEmit(diag)) + return; // Proceed reportReady(diag); diff --git a/src/jdk.crypto.mscapi/windows/native/libsunmscapi/security.cpp b/src/jdk.crypto.mscapi/windows/native/libsunmscapi/security.cpp index ff011dca889..5c84b929ef1 100644 --- a/src/jdk.crypto.mscapi/windows/native/libsunmscapi/security.cpp +++ b/src/jdk.crypto.mscapi/windows/native/libsunmscapi/security.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, 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 @@ -1375,7 +1375,7 @@ JNIEXPORT jobject JNICALL Java_sun_security_mscapi_CKeyPairGenerator_00024RSA_ge PROV_RSA_FULL, CRYPT_NEWKEYSET) == FALSE) { - ThrowException(env, KEY_EXCEPTION, GetLastError()); + ThrowExceptionWithMessageAndErrcode(env, KEY_EXCEPTION, "CryptAcquireContext failure", GetLastError()); __leave; } } @@ -1387,7 +1387,7 @@ JNIEXPORT jobject JNICALL Java_sun_security_mscapi_CKeyPairGenerator_00024RSA_ge dwFlags, &hKeyPair) == FALSE) { - ThrowException(env, KEY_EXCEPTION, GetLastError()); + ThrowExceptionWithMessageAndErrcode(env, KEY_EXCEPTION, "CryptGenKey failure", GetLastError()); __leave; } diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/DwarfParser.cpp b/src/jdk.hotspot.agent/linux/native/libsaproc/DwarfParser.cpp index 62dbc84f88c..cc03f3fc832 100644 --- a/src/jdk.hotspot.agent/linux/native/libsaproc/DwarfParser.cpp +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/DwarfParser.cpp @@ -31,73 +31,54 @@ #define CHECK_EXCEPTION if (env->ExceptionCheck()) { return; } static jfieldID p_dwarf_context_ID = 0; -static jint sa_RAX = -1; -static jint sa_RDX = -1; -static jint sa_RCX = -1; -static jint sa_RBX = -1; -static jint sa_RSI = -1; -static jint sa_RDI = -1; -static jint sa_RBP = -1; -static jint sa_RSP = -1; -static jint sa_R8 = -1; -static jint sa_R9 = -1; -static jint sa_R10 = -1; -static jint sa_R11 = -1; -static jint sa_R12 = -1; -static jint sa_R13 = -1; -static jint sa_R14 = -1; -static jint sa_R15 = -1; + +// DWARF_REG macro is used by DWARF_REGLIST. +#define DWARF_REG(reg, _) \ + static jint sa_##reg = -1; + +DWARF_REGLIST + +#undef DWARF_REG static jlong get_dwarf_context(JNIEnv *env, jobject obj) { return env->GetLongField(obj, p_dwarf_context_ID); } -#define SET_REG(env, reg, reg_cls) \ +/* + * Class: sun_jvm_hotspot_debugger_linux_DwarfParser + * Method: init0 + * Signature: ()V + */ +extern "C" +JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_linux_DwarfParser_init0 + (JNIEnv *env, jclass this_cls) { + jclass cls = env->FindClass("sun/jvm/hotspot/debugger/linux/DwarfParser"); + CHECK_EXCEPTION + p_dwarf_context_ID = env->GetFieldID(cls, "p_dwarf_context", "J"); + CHECK_EXCEPTION + + jclass reg_cls = env->FindClass(THREAD_CONTEXT_CLASS); + CHECK_EXCEPTION + +// DWARF_REG macro is used by DWARF_REGLIST. +#define DWARF_REG(reg, _) \ jfieldID reg##_ID = env->GetStaticFieldID(reg_cls, #reg, "I"); \ CHECK_EXCEPTION \ sa_##reg = env->GetStaticIntField(reg_cls, reg##_ID); \ CHECK_EXCEPTION -/* - * Class: sun_jvm_hotspot_debugger_linux_amd64_DwarfParser - * Method: init0 - * Signature: ()V - */ -extern "C" -JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_linux_amd64_DwarfParser_init0 - (JNIEnv *env, jclass this_cls) { - jclass cls = env->FindClass("sun/jvm/hotspot/debugger/linux/amd64/DwarfParser"); - CHECK_EXCEPTION - p_dwarf_context_ID = env->GetFieldID(cls, "p_dwarf_context", "J"); - CHECK_EXCEPTION + DWARF_REGLIST - jclass reg_cls = env->FindClass("sun/jvm/hotspot/debugger/amd64/AMD64ThreadContext"); - CHECK_EXCEPTION - SET_REG(env, RAX, reg_cls); - SET_REG(env, RDX, reg_cls); - SET_REG(env, RCX, reg_cls); - SET_REG(env, RBX, reg_cls); - SET_REG(env, RSI, reg_cls); - SET_REG(env, RDI, reg_cls); - SET_REG(env, RBP, reg_cls); - SET_REG(env, RSP, reg_cls); - SET_REG(env, R8, reg_cls); - SET_REG(env, R9, reg_cls); - SET_REG(env, R10, reg_cls); - SET_REG(env, R11, reg_cls); - SET_REG(env, R12, reg_cls); - SET_REG(env, R13, reg_cls); - SET_REG(env, R14, reg_cls); - SET_REG(env, R15, reg_cls); +#undef DWARF_REG } /* - * Class: sun_jvm_hotspot_debugger_linux_amd64_DwarfParser + * Class: sun_jvm_hotspot_debugger_linux_DwarfParser * Method: createDwarfContext * Signature: (J)J */ extern "C" -JNIEXPORT jlong JNICALL Java_sun_jvm_hotspot_debugger_linux_amd64_DwarfParser_createDwarfContext +JNIEXPORT jlong JNICALL Java_sun_jvm_hotspot_debugger_linux_DwarfParser_createDwarfContext (JNIEnv *env, jclass this_cls, jlong lib) { DwarfParser *parser = new DwarfParser(reinterpret_cast(lib)); if (!parser->is_parseable()) { @@ -113,36 +94,36 @@ JNIEXPORT jlong JNICALL Java_sun_jvm_hotspot_debugger_linux_amd64_DwarfParser_cr } /* - * Class: sun_jvm_hotspot_debugger_linux_amd64_DwarfParser + * Class: sun_jvm_hotspot_debugger_linux_DwarfParser * Method: destroyDwarfContext * Signature: (J)V */ extern "C" -JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_linux_amd64_DwarfParser_destroyDwarfContext +JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_linux_DwarfParser_destroyDwarfContext (JNIEnv *env, jclass this_cls, jlong context) { DwarfParser *parser = reinterpret_cast(context); delete parser; } /* - * Class: sun_jvm_hotspot_debugger_linux_amd64_DwarfParser + * Class: sun_jvm_hotspot_debugger_linux_DwarfParser * Method: isIn0 * Signature: (J)Z */ extern "C" -JNIEXPORT jboolean JNICALL Java_sun_jvm_hotspot_debugger_linux_amd64_DwarfParser_isIn0 +JNIEXPORT jboolean JNICALL Java_sun_jvm_hotspot_debugger_linux_DwarfParser_isIn0 (JNIEnv *env, jobject this_obj, jlong pc) { DwarfParser *parser = reinterpret_cast(get_dwarf_context(env, this_obj)); return static_cast(parser->is_in(pc)); } /* - * Class: sun_jvm_hotspot_debugger_linux_amd64_DwarfParser + * Class: sun_jvm_hotspot_debugger_linux_DwarfParser * Method: processDwarf0 * Signature: (J)V */ extern "C" -JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_linux_amd64_DwarfParser_processDwarf0 +JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_linux_DwarfParser_processDwarf0 (JNIEnv *env, jobject this_obj, jlong pc) { DwarfParser *parser = reinterpret_cast(get_dwarf_context(env, this_obj)); if (!parser->process_dwarf(pc)) { @@ -155,67 +136,106 @@ JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_linux_amd64_DwarfParser_pro } /* - * Class: sun_jvm_hotspot_debugger_linux_amd64_DwarfParser + * Class: sun_jvm_hotspot_debugger_linux_DwarfParser * Method: getCFARegister * Signature: ()I */ extern "C" -JNIEXPORT jint JNICALL Java_sun_jvm_hotspot_debugger_linux_amd64_DwarfParser_getCFARegister +JNIEXPORT jint JNICALL Java_sun_jvm_hotspot_debugger_linux_DwarfParser_getCFARegister (JNIEnv *env, jobject this_obj) { DwarfParser *parser = reinterpret_cast(get_dwarf_context(env, this_obj)); + switch (parser->get_cfa_register()) { - case RAX: return sa_RAX; - case RDX: return sa_RDX; - case RCX: return sa_RCX; - case RBX: return sa_RBX; - case RSI: return sa_RSI; - case RDI: return sa_RDI; - case RBP: return sa_RBP; - case RSP: return sa_RSP; - case R8: return sa_R8; - case R9: return sa_R9; - case R10: return sa_R10; - case R11: return sa_R11; - case R12: return sa_R12; - case R13: return sa_R13; - case R14: return sa_R14; - case R15: return sa_R15; +// DWARF_REG macro is used by DWARF_REGLIST. +#define DWARF_REG(reg, _) \ + case reg: return sa_##reg; + + DWARF_REGLIST + +#undef DWARF_REG + default: return -1; } } /* - * Class: sun_jvm_hotspot_debugger_linux_amd64_DwarfParser + * Class: sun_jvm_hotspot_debugger_linux_DwarfParser * Method: getCFAOffset * Signature: ()I */ extern "C" -JNIEXPORT jint JNICALL Java_sun_jvm_hotspot_debugger_linux_amd64_DwarfParser_getCFAOffset +JNIEXPORT jint JNICALL Java_sun_jvm_hotspot_debugger_linux_DwarfParser_getCFAOffset (JNIEnv *env, jobject this_obj) { DwarfParser *parser = reinterpret_cast(get_dwarf_context(env, this_obj)); return parser->get_cfa_offset(); } /* - * Class: sun_jvm_hotspot_debugger_linux_amd64_DwarfParser + * Class: sun_jvm_hotspot_debugger_linux_DwarfParser + * Method: getOffsetFromCFA + * Signature: (I)I + */ +extern "C" +JNIEXPORT jint JNICALL Java_sun_jvm_hotspot_debugger_linux_DwarfParser_getOffsetFromCFA + (JNIEnv *env, jobject this_obj, jint sareg) { + DwarfParser *parser = reinterpret_cast(get_dwarf_context(env, this_obj)); + +// DWARF_REG macro is used by DWARF_REGLIST. +#define DWARF_REG(reg, dwreg) \ + if (sareg == sa_##reg) { \ + return parser->get_offset_from_cfa(static_cast(dwreg)); \ + } else + + DWARF_REGLIST + +#undef DWARF_REG + + return INT_MAX; +} + +/* + * Class: sun_jvm_hotspot_debugger_linux_DwarfParser + * Method: getRARegister + * Signature: ()I + */ +extern "C" +JNIEXPORT jint JNICALL Java_sun_jvm_hotspot_debugger_linux_DwarfParser_getRARegister + (JNIEnv *env, jobject this_obj) { + DwarfParser *parser = reinterpret_cast(get_dwarf_context(env, this_obj)); + + switch (parser->get_ra_register()) { +// DWARF_REG macro is used by DWARF_REGLIST. +#define DWARF_REG(reg, _) \ + case reg: return sa_##reg; + + DWARF_REGLIST + +#undef DWARF_REG + + default: return -1; + } +} + +/* + * Class: sun_jvm_hotspot_debugger_linux_DwarfParser * Method: getReturnAddressOffsetFromCFA * Signature: ()I */ extern "C" -JNIEXPORT jint JNICALL Java_sun_jvm_hotspot_debugger_linux_amd64_DwarfParser_getReturnAddressOffsetFromCFA +JNIEXPORT jint JNICALL Java_sun_jvm_hotspot_debugger_linux_DwarfParser_getReturnAddressOffsetFromCFA (JNIEnv *env, jobject this_obj) { DwarfParser *parser = reinterpret_cast(get_dwarf_context(env, this_obj)); return parser->get_offset_from_cfa(RA); } /* - * Class: sun_jvm_hotspot_debugger_linux_amd64_DwarfParser + * Class: sun_jvm_hotspot_debugger_linux_DwarfParser * Method: getBasePointerOffsetFromCFA * Signature: ()I */ extern "C" -JNIEXPORT jint JNICALL Java_sun_jvm_hotspot_debugger_linux_amd64_DwarfParser_getBasePointerOffsetFromCFA +JNIEXPORT jint JNICALL Java_sun_jvm_hotspot_debugger_linux_DwarfParser_getBasePointerOffsetFromCFA (JNIEnv *env, jobject this_obj) { DwarfParser *parser = reinterpret_cast(get_dwarf_context(env, this_obj)); - return parser->get_offset_from_cfa(RBP); + return parser->get_offset_from_cfa(BP); } diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/LinuxDebuggerLocal.cpp b/src/jdk.hotspot.agent/linux/native/libsaproc/LinuxDebuggerLocal.cpp index caf948019af..214e2f21ac6 100644 --- a/src/jdk.hotspot.agent/linux/native/libsaproc/LinuxDebuggerLocal.cpp +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/LinuxDebuggerLocal.cpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2019, 2021, NTT DATA. + * Copyright (c) 2002, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, NTT DATA. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -289,6 +289,13 @@ JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal_at snprintf(msg, sizeof(msg), "Can't attach to the process: %s", err_buf); THROW_NEW_DEBUGGER_EXCEPTION(msg); } + +#ifdef __aarch64__ + if (pac_enabled(ph)) { + printf("WARNING: PAC is enabled. Stack traces might be incomplete.\n"); + } +#endif + env->SetLongField(this_obj, p_ps_prochandle_ID, (jlong)(intptr_t)ph); fillThreadsAndLoadObjects(env, this_obj, ph); } @@ -313,6 +320,13 @@ JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal_at if ( (ph = Pgrab_core(execName_cstr, coreName_cstr)) == NULL) { THROW_NEW_DEBUGGER_EXCEPTION("Can't attach to the core file. For more information, export LIBSAPROC_DEBUG=1 and try again."); } + +#ifdef __aarch64__ + if (pac_enabled(ph)) { + printf("WARNING: PAC is enabled. Stack traces might be incomplete.\n"); + } +#endif + env->SetLongField(this_obj, p_ps_prochandle_ID, (jlong)(intptr_t)ph); fillThreadsAndLoadObjects(env, this_obj, ph); } diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.cpp b/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.cpp index 459e3cc57e9..28eb92a285f 100644 --- a/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.cpp +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.cpp @@ -217,6 +217,20 @@ void DwarfParser::parse_dwarf_instructions(uintptr_t begin, uintptr_t pc, const _state.offset_from_cfa[reg] = _initial_state.offset_from_cfa[reg]; break; } +#ifdef __aarch64__ + // SA hasn't yet supported Pointer Authetication Code (PAC), so following + // instructions would be ignored with warning message. + // https://github.com/ARM-software/abi-aa/blob/2025Q4/aadwarf64/aadwarf64.rst + case 0x2d: // DW_CFA_AARCH64_negate_ra_state + print_debug("DWARF: DW_CFA_AARCH64_negate_ra_state is unimplemented.\n", op); + break; + case 0x2c: // DW_CFA_AARCH64_negate_ra_state_with_pc + print_debug("DWARF: DW_CFA_AARCH64_negate_ra_state_with_pc is unimplemented.\n", op); + break; + case 0x2b: // DW_CFA_AARCH64_set_ra_state + print_debug("DWARF: DW_CFA_AARCH64_set_ra_state is unimplemented.\n", op); + break; +#endif default: print_debug("DWARF: Unknown opcode: 0x%x\n", op); return; diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.hpp b/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.hpp index 0a38c9a0f2e..2bfdba65a78 100644 --- a/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.hpp +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.hpp @@ -30,30 +30,21 @@ #include "libproc_impl.h" -/* - * from System V Application Binary Interface - * AMD64 Architecture Processor Supplement - * Figure 3.38: DWARF Register Number Mapping - * https://software.intel.com/sites/default/files/article/402129/mpx-linux64-abi.pdf - */ +#ifdef __x86_64__ +#include "dwarf_regs_amd64.h" +#elif defined(__aarch64__) +#include "dwarf_regs_aarch64.h" +#endif + enum DWARF_Register { - RAX, - RDX, - RCX, - RBX, - RSI, - RDI, - RBP, - RSP, - R8, - R9, - R10, - R11, - R12, - R13, - R14, - R15, - RA, +// DWARF_REG macro is used by DWARF_REGLIST and DWARF_PSEUDO_REGLIST. +#define DWARF_REG(reg, no) \ + reg = no, + + DWARF_REGLIST + DWARF_PSEUDO_REGLIST + +#undef DWARF_REG MAX_VALUE }; @@ -94,6 +85,7 @@ class DwarfParser { bool process_dwarf(const uintptr_t pc); enum DWARF_Register get_cfa_register() { return _state.cfa_reg; } int get_cfa_offset() { return _state.cfa_offset; } + enum DWARF_Register get_ra_register() { return _state.return_address_reg; } int get_offset_from_cfa(enum DWARF_Register reg) { return _state.offset_from_cfa[reg]; } bool is_in(long pc) { diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf_regs_aarch64.h b/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf_regs_aarch64.h new file mode 100644 index 00000000000..5a95e9405e1 --- /dev/null +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf_regs_aarch64.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, NTT DATA. + * 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. + * + */ + +#ifndef DWARF_REGS_AARCH64_H +#define DWARF_REGS_AARCH64_H + +#define THREAD_CONTEXT_CLASS "sun/jvm/hotspot/debugger/aarch64/AARCH64ThreadContext" + +/* + * from DWARF for the Arm (R) 64-bit Architecture (AArch64) + * https://github.com/ARM-software/abi-aa/blob/2025Q4/aadwarf64/aadwarf64.rst + * 4.1 DWARF register names + */ +#define DWARF_REGLIST \ + DWARF_REG(R0, 0) \ + DWARF_REG(R1, 1) \ + DWARF_REG(R2, 2) \ + DWARF_REG(R3, 3) \ + DWARF_REG(R4, 4) \ + DWARF_REG(R5, 5) \ + DWARF_REG(R6, 6) \ + DWARF_REG(R7, 7) \ + DWARF_REG(R8, 8) \ + DWARF_REG(R9, 9) \ + DWARF_REG(R10, 10) \ + DWARF_REG(R11, 11) \ + DWARF_REG(R12, 12) \ + DWARF_REG(R13, 13) \ + DWARF_REG(R14, 14) \ + DWARF_REG(R15, 15) \ + DWARF_REG(R16, 16) \ + DWARF_REG(R17, 17) \ + DWARF_REG(R18, 18) \ + DWARF_REG(R19, 19) \ + DWARF_REG(R20, 20) \ + DWARF_REG(R21, 21) \ + DWARF_REG(R22, 22) \ + DWARF_REG(R23, 23) \ + DWARF_REG(R24, 24) \ + DWARF_REG(R25, 25) \ + DWARF_REG(R26, 26) \ + DWARF_REG(R27, 27) \ + DWARF_REG(R28, 28) \ + DWARF_REG(FP, 29) \ + DWARF_REG(LR, 30) \ + DWARF_REG(SP, 31) \ + DWARF_REG(PC, 32) + +// RA_SIGN_STATE might be needed in future to handle PAC. +#define DWARF_PSEUDO_REGLIST + +/* Aliases */ +#define BP FP +#define RA LR + +#endif diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf_regs_amd64.h b/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf_regs_amd64.h new file mode 100644 index 00000000000..8226bc0864c --- /dev/null +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf_regs_amd64.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, NTT DATA. + * 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. + * + */ + +#ifndef DWARF_REGS_AMD64_H +#define DWARF_REGS_AMD64_H + +#define THREAD_CONTEXT_CLASS "sun/jvm/hotspot/debugger/amd64/AMD64ThreadContext" + +/* + * from System V Application Binary Interface + * AMD64 Architecture Processor Supplement + * https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf + * Figure 3.36: DWARF Register Number Mapping + */ +#define DWARF_REGLIST \ + DWARF_REG(RAX, 0) \ + DWARF_REG(RDX, 1) \ + DWARF_REG(RCX, 2) \ + DWARF_REG(RBX, 3) \ + DWARF_REG(RSI, 4) \ + DWARF_REG(RDI, 5) \ + DWARF_REG(RBP, 6) \ + DWARF_REG(RSP, 7) \ + DWARF_REG(R8, 8) \ + DWARF_REG(R9, 9) \ + DWARF_REG(R10, 10) \ + DWARF_REG(R11, 11) \ + DWARF_REG(R12, 12) \ + DWARF_REG(R13, 13) \ + DWARF_REG(R14, 14) \ + DWARF_REG(R15, 15) + +#define DWARF_PSEUDO_REGLIST \ + DWARF_REG(RA, 16) + +/* Aliases */ +#define BP RBP + +#endif diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/libproc.h b/src/jdk.hotspot.agent/linux/native/libsaproc/libproc.h index a69496e77a4..c584131e285 100644 --- a/src/jdk.hotspot.agent/linux/native/libsaproc/libproc.h +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/libproc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -119,6 +119,10 @@ struct ps_prochandle* get_proc_handle(JNIEnv* env, jobject this_obj); void throw_new_debugger_exception(JNIEnv* env, const char* errMsg); +#ifdef __aarch64__ +bool pac_enabled(struct ps_prochandle* ph); +#endif + #ifdef __cplusplus } #endif diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.c b/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.c index e2681be73fe..815902045cf 100644 --- a/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.c +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.c @@ -182,13 +182,14 @@ static bool fill_addr_info(lib_info* lib) { return false; } + long page_size = sysconf(_SC_PAGE_SIZE); lib->end = (uintptr_t)-1L; lib->exec_start = (uintptr_t)-1L; lib->exec_end = (uintptr_t)-1L; for (ph = phbuf, cnt = 0; cnt < ehdr.e_phnum; cnt++, ph++) { if (ph->p_type == PT_LOAD) { uintptr_t aligned_start = lib->base + align_down(ph->p_vaddr, ph->p_align); - uintptr_t aligned_end = aligned_start + align_up(ph->p_memsz, ph->p_align); + uintptr_t aligned_end = aligned_start + align_up(ph->p_memsz, page_size); if ((lib->end == (uintptr_t)-1L) || (lib->end < aligned_end)) { lib->end = aligned_end; } @@ -477,6 +478,12 @@ struct lib_info *find_lib_by_address(struct ps_prochandle* ph, uintptr_t pc) { return NULL; } +#ifdef __aarch64__ +bool pac_enabled(struct ps_prochandle* ph) { + return ph->pac_enabled; +} +#endif + //-------------------------------------------------------------------------- // proc service functions diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.h b/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.h index 62b1b4d0d6b..d5aa74e73ad 100644 --- a/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.h +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.h @@ -115,6 +115,10 @@ struct ps_prochandle { int num_threads; thread_info* threads; // head of thread list struct core_data* core; // data only used for core dumps, NULL for process +#ifdef __aarch64__ + // true if the HWCAP_PACA variant of Pointer Authentication Code (PAC) is enabled. + bool pac_enabled; +#endif }; #ifdef __cplusplus diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/ps_core.c b/src/jdk.hotspot.agent/linux/native/libsaproc/ps_core.c index 6298f569aaf..c500360f39d 100644 --- a/src/jdk.hotspot.agent/linux/native/libsaproc/ps_core.c +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/ps_core.c @@ -39,6 +39,12 @@ #include "proc_service.h" #include "salibelf.h" +// HWCAP_PACA was introduced in glibc 2.30 +// https://sourceware.org/git/?p=glibc.git;a=commit;h=a2e57f89a35e6056c9488428e68c4889e114ef71 +#if defined(__aarch64__) && !defined(HWCAP_PACA) +#define HWCAP_PACA (1 << 30) +#endif + // This file has the libproc implementation to read core files. // For live processes, refer to ps_proc.c. Portions of this is adapted // /modelled after Solaris libproc.so (in particular Pcore.c) @@ -290,6 +296,10 @@ static bool core_handle_note(struct ps_prochandle* ph, ELF_PHDR* note_phdr) { break; } else if (auxv->a_type == AT_SYSINFO_EHDR) { ph->core->vdso_addr = auxv->a_un.a_val; +#ifdef __aarch64__ + } else if (auxv->a_type == AT_HWCAP) { + ph->pac_enabled = auxv->a_un.a_val & HWCAP_PACA; +#endif } auxv++; } @@ -610,23 +620,38 @@ static uintptr_t calc_prelinked_load_address(struct ps_prochandle* ph, int lib_f return load_addr; } +static int handle_vdso_internal(struct ps_prochandle* ph, char* vdso_name, char* lib_name, size_t lib_name_len) { + int lib_fd; + struct utsname uts; + uname(&uts); + + char *vdso_path = (char*)malloc(lib_name_len); + snprintf(vdso_path, lib_name_len, "/lib/modules/%s/vdso/%s", uts.release, vdso_name); + print_debug("Try to open vDSO: %s\n", vdso_path); + lib_fd = pathmap_open(vdso_path); + if (lib_fd != -1) { + print_debug("replace vDSO: %s -> %s\n", lib_name, vdso_path); + strncpy(lib_name, vdso_path, lib_name_len); + } + + free(vdso_path); + return lib_fd; +} + // Check for vDSO binary in kernel directory (/lib/modules//vdso), // rewrite the given lib_name string if found. // Otherwise copy vDSO memory in coredump to temporal file generated by tmpfile(). // Returns FD for vDSO (should be closed by caller), or -1 on error. static int handle_vdso(struct ps_prochandle* ph, char* lib_name, size_t lib_name_len) { int lib_fd; - struct utsname uts; - uname(&uts); // Check vDSO binary first (for referring debuginfo if possible). - char *vdso_path = (char*)malloc(lib_name_len); - snprintf(vdso_path, lib_name_len, "/lib/modules/%s/vdso/vdso64.so", uts.release); - lib_fd = pathmap_open(vdso_path); - if (lib_fd != -1) { - print_debug("replace vDSO: %s -> %s\n", lib_name, vdso_path); - strncpy(lib_name, vdso_path, lib_name_len); - } else { + lib_fd = handle_vdso_internal(ph, "vdso64.so", lib_name, lib_name_len); + if (lib_fd == -1) { + // Try again with vdso.so + lib_fd = handle_vdso_internal(ph, "vdso.so", lib_name, lib_name_len); + } + if (lib_fd == -1) { // Copy vDSO memory segment from core to temporal memory // if vDSO binary is not available. FILE* tmpf = tmpfile(); @@ -644,7 +669,6 @@ static int handle_vdso(struct ps_prochandle* ph, char* lib_name, size_t lib_name } } - free(vdso_path); return lib_fd; } diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/ps_proc.c b/src/jdk.hotspot.agent/linux/native/libsaproc/ps_proc.c index fdaa30c3f5d..9cbde7319f0 100644 --- a/src/jdk.hotspot.agent/linux/native/libsaproc/ps_proc.c +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/ps_proc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -36,6 +36,17 @@ #include #include "libproc_impl.h" +#ifdef __aarch64__ +#include + +// HWCAP_PACA was introduced in glibc 2.30 +// https://sourceware.org/git/?p=glibc.git;a=commit;h=a2e57f89a35e6056c9488428e68c4889e114ef71 +#ifndef HWCAP_PACA +#define HWCAP_PACA (1 << 30) +#endif + +#endif + #if defined(x86_64) && !defined(amd64) #define amd64 1 #endif @@ -460,6 +471,10 @@ Pgrab(pid_t pid, char* err_buf, size_t err_buf_len) { return NULL; } +#ifdef __aarch64__ + ph->pac_enabled = HWCAP_PACA & getauxval(AT_HWCAP); +#endif + // initialize ps_prochandle ph->pid = pid; if (add_thread_info(ph, ph->pid) == NULL) { diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/symtab.c b/src/jdk.hotspot.agent/linux/native/libsaproc/symtab.c index c8f3fb2ed4c..8f8ce28be1e 100644 --- a/src/jdk.hotspot.agent/linux/native/libsaproc/symtab.c +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/symtab.c @@ -417,10 +417,14 @@ static struct symtab* build_symtab_internal(int fd, const char *filename, bool t uintptr_t sym_value; char *sym_name = symtab->strs + syms->st_name; - // skip non-object and non-function symbols + // skip non-object and non-function symbols, but STT_NOTYPE is allowed for + // signal trampoline. int st_type = ELF_ST_TYPE(syms->st_info); - if ( st_type != STT_FUNC && st_type != STT_OBJECT) + if (st_type != STT_FUNC && + st_type != STT_OBJECT && + st_type != STT_NOTYPE) { continue; + } // skip empty strings and undefined symbols if (*sym_name == '\0' || syms->st_shndx == SHN_UNDEF) continue; diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/cdbg/CFrame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/cdbg/CFrame.java index bc366ef02b5..86f9e990af8 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/cdbg/CFrame.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/cdbg/CFrame.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, 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 @@ -39,7 +39,7 @@ public interface CFrame { public CFrame sender(ThreadProxy th); /** Find sender frame with given FP and PC */ - public default CFrame sender(ThreadProxy th, Address sp, Address fp, Address pc) { + public default CFrame sender(ThreadProxy th, Address senderSP, Address senderFP, Address senderPC) { return sender(th); } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/DwarfCFrame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/DwarfCFrame.java new file mode 100644 index 00000000000..7baa399ea75 --- /dev/null +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/DwarfCFrame.java @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, NTT DATA. + * 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. + * + */ + +package sun.jvm.hotspot.debugger.linux; + +import sun.jvm.hotspot.debugger.Address; +import sun.jvm.hotspot.debugger.ThreadProxy; +import sun.jvm.hotspot.debugger.UnalignedAddressException; +import sun.jvm.hotspot.debugger.UnmappedAddressException; +import sun.jvm.hotspot.debugger.cdbg.CFrame; +import sun.jvm.hotspot.debugger.cdbg.ClosestSymbol; +import sun.jvm.hotspot.debugger.cdbg.basic.BasicCFrame; +import sun.jvm.hotspot.runtime.VM; + +public class DwarfCFrame extends BasicCFrame { + + private Address sp; + private Address fp; + private Address pc; + private Address cfa; + private LinuxDebugger linuxDbg; + private DwarfParser dwarf; + private boolean use1ByteBeforeToLookup; + + /** + * @return DwarfParser instance for the PC, null if native library relates to the pc not found. + * @throws DebuggerException if DWARF processing is failed. + * For example: pc is not covered in this DWARF, Common Information Entry (CIE) has + * language personality routine and/or Language Data Area (LSDA). + */ + protected static DwarfParser createDwarfParser(LinuxDebugger linuxDbg, Address pc) { + Address libptr = linuxDbg.findLibPtrByAddress(pc); + if (libptr != null) { + DwarfParser dwarf = new DwarfParser(libptr); + dwarf.processDwarf(pc); + return dwarf; + } + return null; + } + + protected DwarfCFrame(LinuxDebugger linuxDbg, Address sp, Address fp, Address cfa, Address pc, DwarfParser dwarf) { + this(linuxDbg, sp, fp, cfa, pc, dwarf, false); + } + + protected DwarfCFrame(LinuxDebugger linuxDbg, Address sp, Address fp, Address cfa, Address pc, DwarfParser dwarf, boolean use1ByteBeforeToLookup) { + super(linuxDbg.getCDebugger()); + this.sp = sp; + this.fp = fp; + this.cfa = cfa; + this.pc = pc; + this.linuxDbg = linuxDbg; + this.dwarf = dwarf; + this.use1ByteBeforeToLookup = use1ByteBeforeToLookup; + } + + public Address sp() { + return sp; + } + + public Address fp() { + return fp; + } + + public Address pc() { + return pc; + } + + public LinuxDebugger linuxDbg() { + return linuxDbg; + } + + public DwarfParser dwarf() { + return dwarf; + } + + // override base class impl to avoid ELF parsing + @Override + public ClosestSymbol closestSymbolToPC() { + Address symAddr = use1ByteBeforeToLookup ? pc.addOffsetTo(-1) : pc; + var sym = linuxDbg.lookup(linuxDbg.getAddressValue(symAddr)); + + // Returns a special symbol if the address is signal trampoline, + // otherwise returns closest symbol generated by LinuxDebugger. + return linuxDbg.isSignalTrampoline(symAddr) + ? new ClosestSymbol(sym.getName() + " ", 0) + : sym; + } + + @Override + public Address localVariableBase() { + return (dwarf != null && dwarf.isBPOffsetAvailable()) + ? cfa.addOffsetTo(dwarf.getBasePointerOffsetFromCFA()) + : fp; + } + + protected boolean isValidFrame(Address senderCFA, Address senderFP) { + // Both CFA and FP must not be null. + if (senderCFA == null && senderFP == null) { + return false; + } + + // FP must not be null if CFA is null - it happens between Java frame and Native frame. + // We cannot validate FP value because it might be used as GPR. Thus returns true + // if FP is not null. + if (senderCFA == null && senderFP != null) { + return true; + } + + // senderCFA must be greater than current CFA. + if (senderCFA != null && senderCFA.greaterThanOrEqual(cfa)) { + return true; + } + + // Otherwise, the frame is not valid. + return false; + } + + protected Address getSenderPC(Address senderPC) { + if (senderPC != null) { + return senderPC; + } + + try { + return dwarf == null + ? fp.getAddressAt(VM.getVM().getAddressSize()) // Current frame is Java + : cfa.getAddressAt(dwarf.getReturnAddressOffsetFromCFA()); // current frame is Native + } catch (UnmappedAddressException | UnalignedAddressException _) { + // Sender PC is invalid - maybe bottom of stack + return null; + } + } + + protected Address getSenderSP(Address senderSP) { + if (senderSP != null) { + return senderSP; + } else if (dwarf == null) { + // Current frame is Java - skip saved BP and RA + return fp.addOffsetTo(2 * VM.getVM().getAddressSize()); + } else { + // Current frame is Native + // CFA points SP at the call site in the previous frame. + // See 6.4 Call Frame Information in DWARF Debugging Information Format + // https://dwarfstd.org/dwarf4std.html + return cfa; + } + } + + protected Address getSenderFP(Address senderFP) { + if (senderFP != null) { + return senderFP; + } else if (dwarf == null) { // Current frame is Java + return fp.getAddressAt(0); + } else { // Current frame is Native + return dwarf.isBPOffsetAvailable() + ? cfa.getAddressAt(dwarf.getBasePointerOffsetFromCFA()) + : fp; + } + } + + @Override + public CFrame sender(ThreadProxy th) { + return sender(th, null, null, null); + } + +} diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/amd64/DwarfParser.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/DwarfParser.java similarity index 95% rename from src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/amd64/DwarfParser.java rename to src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/DwarfParser.java index 53351c918d3..3e8099cf75b 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/amd64/DwarfParser.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/DwarfParser.java @@ -23,7 +23,7 @@ * */ -package sun.jvm.hotspot.debugger.linux.amd64; +package sun.jvm.hotspot.debugger.linux; import java.lang.ref.Cleaner; import sun.jvm.hotspot.debugger.Address; @@ -75,6 +75,8 @@ public class DwarfParser { public native int getCFARegister(); public native int getCFAOffset(); + public native int getOffsetFromCFA(int sareg); + public native int getRARegister(); public native int getReturnAddressOffsetFromCFA(); public native int getBasePointerOffsetFromCFA(); } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxCDebugger.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxCDebugger.java index 15f6615421c..57ba419aa9d 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxCDebugger.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxCDebugger.java @@ -91,13 +91,7 @@ class LinuxCDebugger implements CDebugger { return new LinuxPPC64CFrame(dbg, sp, pc, LinuxDebuggerLocal.getAddressSize()); } else if (cpu.equals("aarch64")) { AARCH64ThreadContext context = (AARCH64ThreadContext) thread.getContext(); - Address sp = context.getRegisterAsAddress(AARCH64ThreadContext.SP); - if (sp == null) return null; - Address fp = context.getRegisterAsAddress(AARCH64ThreadContext.FP); - if (fp == null) return null; - Address pc = context.getRegisterAsAddress(AARCH64ThreadContext.PC); - if (pc == null) return null; - return new LinuxAARCH64CFrame(dbg, sp, fp, pc); + return LinuxAARCH64CFrame.getTopFrame(dbg, context); } else if (cpu.equals("riscv64")) { RISCV64ThreadContext context = (RISCV64ThreadContext) thread.getContext(); Address sp = context.getRegisterAsAddress(RISCV64ThreadContext.SP); diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxDebugger.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxDebugger.java index a53b8a0a282..c09af9881dd 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxDebugger.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxDebugger.java @@ -33,12 +33,17 @@ import sun.jvm.hotspot.debugger.cdbg.*; by the architecture-specific subpackages. */ public interface LinuxDebugger extends JVMDebugger { - // SIGHANDLER_NAMES holds the name of signal handler. - public static final List SIGHANDLER_NAMES = List.of( + // SIGTRAMP_NAMES holds the name of signal trampoline. + public static final List SIGTRAMP_NAMES = List.of( // For AMD64 // - sysdeps/unix/sysv/linux/x86_64/libc_sigaction.c in glibc // - gdb/amd64-linux-tdep.c in GDB - "__restore_rt" + "__restore_rt", + + // For AArch64 + // - arch/arm64/kernel/vdso/vdso.lds.S in Linux kernel + "__kernel_rt_sigreturn", + "VDSO_sigtramp" ); public String addressValueToString(long address) throws DebuggerException; diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxDebuggerLocal.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxDebuggerLocal.java index 9a75511e44d..856981bb73c 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxDebuggerLocal.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxDebuggerLocal.java @@ -133,7 +133,7 @@ public class LinuxDebuggerLocal extends DebuggerBase implements LinuxDebugger { @Override public boolean isSignalTrampoline(Address pc) { var sym = lookup(getAddressValue(pc)); - return sym == null ? false : SIGHANDLER_NAMES.contains(sym.getName()); + return sym == null ? false : SIGTRAMP_NAMES.contains(sym.getName()); } // Note on Linux threads are really processes. When target process is diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/aarch64/LinuxAARCH64CFrame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/aarch64/LinuxAARCH64CFrame.java index 5f76e6308e9..c55aca2155c 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/aarch64/LinuxAARCH64CFrame.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/aarch64/LinuxAARCH64CFrame.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2015, Red Hat Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -25,73 +25,109 @@ package sun.jvm.hotspot.debugger.linux.aarch64; +import java.util.function.Function; + import sun.jvm.hotspot.debugger.*; import sun.jvm.hotspot.debugger.aarch64.*; import sun.jvm.hotspot.debugger.linux.*; import sun.jvm.hotspot.debugger.cdbg.*; -import sun.jvm.hotspot.debugger.cdbg.basic.*; import sun.jvm.hotspot.code.*; import sun.jvm.hotspot.runtime.*; import sun.jvm.hotspot.runtime.aarch64.*; -public final class LinuxAARCH64CFrame extends BasicCFrame { - public LinuxAARCH64CFrame(LinuxDebugger dbg, Address sp, Address fp, Address pc) { - super(dbg.getCDebugger()); - this.sp = sp; - this.fp = fp; - this.pc = pc; - this.dbg = dbg; +public final class LinuxAARCH64CFrame extends DwarfCFrame { + + private Address lr; + + private static LinuxAARCH64CFrame getFrameFromReg(LinuxDebugger linuxDbg, Function getreg) { + Address pc = getreg.apply(AARCH64ThreadContext.PC); + Address sp = getreg.apply(AARCH64ThreadContext.SP); + Address fp = getreg.apply(AARCH64ThreadContext.FP); + Address lr = getreg.apply(AARCH64ThreadContext.LR); + Address cfa = null; + DwarfParser dwarf = createDwarfParser(linuxDbg, pc); + + if (dwarf != null) { // Native frame + cfa = getreg.apply(dwarf.getCFARegister()) + .addOffsetTo(dwarf.getCFAOffset()); + } + + return (fp == null && cfa == null) + ? null + : new LinuxAARCH64CFrame(linuxDbg, sp, fp, cfa, pc, lr, dwarf); } - // override base class impl to avoid ELF parsing - public ClosestSymbol closestSymbolToPC() { - // try native lookup in debugger. - return dbg.lookup(dbg.getAddressValue(pc())); + public static LinuxAARCH64CFrame getTopFrame(LinuxDebugger linuxDbg, ThreadContext context) { + return getFrameFromReg(linuxDbg, context::getRegisterAsAddress); } - public Address pc() { - return pc; + private LinuxAARCH64CFrame(LinuxDebugger linuxDbg, Address sp, Address fp, Address cfa, Address pc, Address lr, DwarfParser dwarf) { + this(linuxDbg, sp, fp, cfa, pc, lr, dwarf, false); } - public Address localVariableBase() { - return fp; + private LinuxAARCH64CFrame(LinuxDebugger linuxDbg, Address sp, Address fp, Address cfa, Address pc, DwarfParser dwarf) { + this(linuxDbg, sp, fp, cfa, pc, null, dwarf, false); } - @Override - public CFrame sender(ThreadProxy thread) { - return sender(thread, null, null, null); + private LinuxAARCH64CFrame(LinuxDebugger linuxDbg, Address sp, Address fp, Address cfa, Address pc, DwarfParser dwarf, boolean use1ByteBeforeToLookup) { + this(linuxDbg, sp, fp, cfa, pc, null, dwarf, use1ByteBeforeToLookup); } - @Override - public CFrame sender(ThreadProxy thread, Address nextSP, Address nextFP, Address nextPC) { - // Check fp - // Skip if both nextFP and nextPC are given - do not need to load from fp. - if (nextFP == null && nextPC == null) { - if (fp == null) { - return null; + private LinuxAARCH64CFrame(LinuxDebugger linuxDbg, Address sp, Address fp, Address cfa, Address pc, Address lr, DwarfParser dwarf, boolean use1ByteBeforeToLookup) { + super(linuxDbg, sp, fp, cfa, pc, dwarf, use1ByteBeforeToLookup); + + if (dwarf != null) { + // Prioritize to use RA from DWARF instead of LR + var senderPCFromDwarf = getSenderPC(null); + if (senderPCFromDwarf != null) { + lr = senderPCFromDwarf; + } else if (lr != null) { + // We should set passed lr to LR of this frame, + // but throws DebuggerException if lr is not used for RA. + var raReg = dwarf.getRARegister(); + if (raReg != AARCH64ThreadContext.LR) { + throw new DebuggerException("Unexpected RA register: " + raReg); + } } + } - // Check alignment of fp - if (dbg.getAddressValue(fp) % (2 * ADDRESS_SIZE) != 0) { + this.lr = lr; + } + + private Address getSenderCFA(DwarfParser senderDwarf, Address senderSP, Address senderFP) { + if (senderDwarf == null) { // Sender frame is Java + // CFA is not available on Java frame + return null; + } + + // Sender frame is Native + int senderCFAReg = senderDwarf.getCFARegister(); + return switch(senderCFAReg){ + case AARCH64ThreadContext.FP -> senderFP.addOffsetTo(senderDwarf.getCFAOffset()); + case AARCH64ThreadContext.SP -> senderSP.addOffsetTo(senderDwarf.getCFAOffset()); + default -> throw new DebuggerException("Unsupported CFA register: " + senderCFAReg); + }; + } + + @Override + public CFrame sender(ThreadProxy thread, Address senderSP, Address senderFP, Address senderPC) { + if (linuxDbg().isSignalTrampoline(pc())) { + // SP points signal context + // https://github.com/torvalds/linux/blob/v6.17/arch/arm64/kernel/signal.c#L1357 + return getFrameFromReg(linuxDbg(), r -> LinuxAARCH64ThreadContext.getRegFromSignalTrampoline(sp(), r.intValue())); + } + + if (senderPC == null) { + // Use getSenderPC() if current frame is Java because we cannot rely on lr in this case. + senderPC = dwarf() == null ? getSenderPC(null) : lr; + if (senderPC == null) { return null; } } - if (nextFP == null) { - nextFP = fp.getAddressAt(0 * ADDRESS_SIZE); - } - if (nextFP == null) { - return null; - } + senderFP = getSenderFP(senderFP); - if (nextPC == null) { - nextPC = fp.getAddressAt(1 * ADDRESS_SIZE); - } - if (nextPC == null) { - return null; - } - - if (nextSP == null) { + if (senderSP == null) { CodeCache cc = VM.getVM().getCodeCache(); CodeBlob currentBlob = cc.findBlobUnsafe(pc()); @@ -99,29 +135,62 @@ public final class LinuxAARCH64CFrame extends BasicCFrame { if (currentBlob != null && (currentBlob.isContinuationStub() || currentBlob.isNativeMethod())) { // Use FP since it should always be valid for these cases. // TODO: These should be walked as Frames not CFrames. - nextSP = fp.addOffsetTo(2 * ADDRESS_SIZE); + senderSP = fp().addOffsetTo(2 * VM.getVM().getAddressSize()); } else { - CodeBlob codeBlob = cc.findBlobUnsafe(nextPC); + CodeBlob codeBlob = cc.findBlobUnsafe(senderPC); boolean useCodeBlob = codeBlob != null && codeBlob.getFrameSize() > 0; - nextSP = useCodeBlob ? nextFP.addOffsetTo((2 * ADDRESS_SIZE) - codeBlob.getFrameSize()) : nextFP; + senderSP = useCodeBlob ? senderFP.addOffsetTo((2 * VM.getVM().getAddressSize()) - codeBlob.getFrameSize()) : getSenderSP(null); } } - if (nextSP == null) { + if (senderSP == null) { return null; } - return new LinuxAARCH64CFrame(dbg, nextSP, nextFP, nextPC); + DwarfParser senderDwarf = null; + boolean fallback = false; + try { + senderDwarf = createDwarfParser(linuxDbg(), senderPC); + } catch (DebuggerException _) { + // Try again with PC-1 in case PC is just outside function bounds, + // due to function ending with a `call` instruction. + try { + senderDwarf = createDwarfParser(linuxDbg(), senderPC.addOffsetTo(-1)); + fallback = true; + } catch (DebuggerException _) { + if (linuxDbg().isSignalTrampoline(senderPC)) { + // We can use the caller frame if it is a signal trampoline. + // DWARF processing might fail because vdso.so .eh_frame is not required on aarch64. + return new LinuxAARCH64CFrame(linuxDbg(), senderSP, senderFP, null, senderPC, senderDwarf); + } + + // DWARF processing should succeed when the frame is native + // but it might fail if Common Information Entry (CIE) has language + // personality routine and/or Language Specific Data Area (LSDA). + return null; + } + } + + try { + Address senderCFA = getSenderCFA(senderDwarf, senderSP, senderFP); + return isValidFrame(senderCFA, senderFP) + ? new LinuxAARCH64CFrame(linuxDbg(), senderSP, senderFP, senderCFA, senderPC, senderDwarf, fallback) + : null; + } catch (DebuggerException e) { + if (linuxDbg().isSignalTrampoline(senderPC)) { + // We can use the caller frame if it is a signal trampoline. + // getSenderCFA() might fail because DwarfParser cannot find out CFA register. + return new LinuxAARCH64CFrame(linuxDbg(), senderSP, senderFP, null, senderPC, senderDwarf, fallback); + } + + // Rethrow the original exception if getSenderCFA() failed + // and the caller is not signal trampoline. + throw e; + } } @Override public Frame toFrame() { - return new AARCH64Frame(sp, fp, pc); + return new AARCH64Frame(sp(), fp(), pc()); } - // package/class internals only - private static final int ADDRESS_SIZE = 8; - private Address pc; - private Address sp; - private Address fp; - private LinuxDebugger dbg; } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/aarch64/LinuxAARCH64ThreadContext.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/aarch64/LinuxAARCH64ThreadContext.java index 77003168671..422ca001624 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/aarch64/LinuxAARCH64ThreadContext.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/aarch64/LinuxAARCH64ThreadContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2015, Red Hat Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -28,6 +28,7 @@ package sun.jvm.hotspot.debugger.linux.aarch64; import sun.jvm.hotspot.debugger.*; import sun.jvm.hotspot.debugger.aarch64.*; import sun.jvm.hotspot.debugger.linux.*; +import sun.jvm.hotspot.runtime.*; public class LinuxAARCH64ThreadContext extends AARCH64ThreadContext { private LinuxDebugger debugger; @@ -44,4 +45,24 @@ public class LinuxAARCH64ThreadContext extends AARCH64ThreadContext { public Address getRegisterAsAddress(int index) { return debugger.newAddress(getRegister(index)); } + + public static Address getRegFromSignalTrampoline(Address sp, int index) { + // ucontext_t locates at 2nd element of rt_sigframe. + // See definition of rt_sigframe in arch/arm/kernel/signal.h + // in Linux Kernel. + Address addrUContext = sp.addOffsetTo(128); // sizeof(siginfo_t) = 128 + Address addrUCMContext = addrUContext.addOffsetTo(176); // offsetof(ucontext_t, uc_mcontext) = 176 + + Address ptrCallerSP = addrUCMContext.addOffsetTo(256); // offsetof(uc_mcontext, sp) = 256 + Address ptrCallerPC = addrUCMContext.addOffsetTo(264); // offsetof(uc_mcontext, pc) = 264 + Address ptrCallerRegs = addrUCMContext.addOffsetTo(8); // offsetof(uc_mcontext, regs) = 8 + + return switch(index) { + case AARCH64ThreadContext.FP -> ptrCallerRegs.getAddressAt(AARCH64ThreadContext.FP * VM.getVM().getAddressSize()); + case AARCH64ThreadContext.LR -> ptrCallerRegs.getAddressAt(AARCH64ThreadContext.LR * VM.getVM().getAddressSize()); + case AARCH64ThreadContext.SP -> ptrCallerSP.getAddressAt(0); + case AARCH64ThreadContext.PC -> ptrCallerPC.getAddressAt(0); + default -> throw new IllegalArgumentException("Unsupported register index: " + index); + }; + } } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/amd64/LinuxAMD64CFrame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/amd64/LinuxAMD64CFrame.java index 4d3d9d5998d..e58e2facdd7 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/amd64/LinuxAMD64CFrame.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/amd64/LinuxAMD64CFrame.java @@ -29,181 +29,82 @@ import java.util.function.Function; import sun.jvm.hotspot.debugger.*; import sun.jvm.hotspot.debugger.amd64.*; import sun.jvm.hotspot.debugger.linux.*; -import sun.jvm.hotspot.debugger.linux.amd64.*; import sun.jvm.hotspot.debugger.cdbg.*; -import sun.jvm.hotspot.debugger.cdbg.basic.*; import sun.jvm.hotspot.runtime.*; import sun.jvm.hotspot.runtime.amd64.*; -public final class LinuxAMD64CFrame extends BasicCFrame { +public final class LinuxAMD64CFrame extends DwarfCFrame { - private static LinuxAMD64CFrame getFrameFromReg(LinuxDebugger dbg, Function getreg) { + private static LinuxAMD64CFrame getFrameFromReg(LinuxDebugger linuxDbg, Function getreg) { Address rip = getreg.apply(AMD64ThreadContext.RIP); Address rsp = getreg.apply(AMD64ThreadContext.RSP); Address rbp = getreg.apply(AMD64ThreadContext.RBP); - Address libptr = dbg.findLibPtrByAddress(rip); Address cfa = null; - DwarfParser dwarf = null; - - if (libptr != null) { // Native frame - dwarf = new DwarfParser(libptr); - try { - dwarf.processDwarf(rip); - } catch (DebuggerException e) { - // DWARF processing should succeed when the frame is native - // but it might fail if Common Information Entry (CIE) has language - // personality routine and/or Language Specific Data Area (LSDA). - return new LinuxAMD64CFrame(dbg, rsp, rbp, cfa, rip, dwarf, true); - } + DwarfParser dwarf = createDwarfParser(linuxDbg, rip); + if (dwarf != null) { // Native frame cfa = getreg.apply(dwarf.getCFARegister()) .addOffsetTo(dwarf.getCFAOffset()); } return (rbp == null && cfa == null) ? null - : new LinuxAMD64CFrame(dbg, rsp, rbp, cfa, rip, dwarf); + : new LinuxAMD64CFrame(linuxDbg, rsp, rbp, cfa, rip, dwarf); } - public static LinuxAMD64CFrame getTopFrame(LinuxDebugger dbg, ThreadContext context) { - return getFrameFromReg(dbg, context::getRegisterAsAddress); + public static LinuxAMD64CFrame getTopFrame(LinuxDebugger linuxDbg, ThreadContext context) { + return getFrameFromReg(linuxDbg, context::getRegisterAsAddress); } - private LinuxAMD64CFrame(LinuxDebugger dbg, Address rsp, Address rbp, Address cfa, Address rip, DwarfParser dwarf) { - this(dbg, rsp, rbp, cfa, rip, dwarf, false); + private LinuxAMD64CFrame(LinuxDebugger linuxDbg, Address rsp, Address rbp, Address cfa, Address rip, DwarfParser dwarf) { + this(linuxDbg, rsp, rbp, cfa, rip, dwarf, false); } - private LinuxAMD64CFrame(LinuxDebugger dbg, Address rsp, Address rbp, Address cfa, Address rip, DwarfParser dwarf, boolean use1ByteBeforeToLookup) { - super(dbg.getCDebugger()); - this.rsp = rsp; - this.rbp = rbp; - this.cfa = cfa; - this.rip = rip; - this.dbg = dbg; - this.dwarf = dwarf; - this.use1ByteBeforeToLookup = use1ByteBeforeToLookup; + private LinuxAMD64CFrame(LinuxDebugger linuxDbg, Address rsp, Address rbp, Address cfa, Address rip, DwarfParser dwarf, boolean use1ByteBeforeToLookup) { + super(linuxDbg, rsp, rbp, cfa, rip, dwarf, use1ByteBeforeToLookup); } - // override base class impl to avoid ELF parsing - public ClosestSymbol closestSymbolToPC() { - Address symAddr = use1ByteBeforeToLookup ? pc().addOffsetTo(-1) : pc(); - var sym = dbg.lookup(dbg.getAddressValue(symAddr)); - - // Returns a special symbol if the address is signal handler, - // otherwise returns closest symbol generated by LinuxDebugger. - return dbg.isSignalTrampoline(symAddr) - ? new ClosestSymbol(sym.getName() + " ", 0) - : sym; - } - - public Address pc() { - return rip; - } - - public Address localVariableBase() { - return (dwarf != null && dwarf.isBPOffsetAvailable()) - ? cfa.addOffsetTo(dwarf.getBasePointerOffsetFromCFA()) - : rbp; - } - - private Address getNextPC() { - try { - return dwarf == null - ? rbp.getAddressAt(ADDRESS_SIZE) // Java frame - : cfa.getAddressAt(dwarf.getReturnAddressOffsetFromCFA()); // Native frame - } catch (UnmappedAddressException | UnalignedAddressException e) { - return null; - } - } - - private boolean isValidFrame(Address nextCFA, Address nextRBP) { - // Both CFA and RBP must not be null. - if (nextCFA == null && nextRBP == null) { - return false; - } - - // RBP must not be null if CFA is null - it happens between Java frame and Native frame. - // We cannot validate RBP value because it might be used as GPR. Thus returns true - // if RBP is not null. - if (nextCFA == null && nextRBP != null) { - return true; - } - - // nextCFA must be greater than current CFA. - if (nextCFA != null && nextCFA.greaterThanOrEqual(cfa)) { - return true; - } - - // Otherwise, the frame is not valid. - return false; - } - - private Address getNextRSP() { - return dwarf == null ? rbp.addOffsetTo(2 * ADDRESS_SIZE) // Java frame - skip saved BP and RA - : cfa.addOffsetTo(dwarf.getReturnAddressOffsetFromCFA()) - .addOffsetTo(ADDRESS_SIZE); // Native frame - } - - private Address getNextRBP(Address senderFP) { - if (senderFP != null) { - return senderFP; - } else if (dwarf == null) { // Current frame is Java - return rbp.getAddressAt(0); - } else { // Current frame is Native - return dwarf.isBPOffsetAvailable() - ? cfa.getAddressAt(dwarf.getBasePointerOffsetFromCFA()) - : rbp; - } - } - - private Address getNextCFA(DwarfParser nextDwarf, Address senderFP, Address senderPC) { - if (nextDwarf == null) { // Next frame is Java + private Address getSenderCFA(DwarfParser senderDwarf, Address senderSP, Address senderFP) { + if (senderDwarf == null) { // Sender frame is Java // CFA is not available on Java frame return null; } - // Next frame is Native - int nextCFAReg = nextDwarf.getCFARegister(); - return switch(nextCFAReg){ - case AMD64ThreadContext.RBP -> getNextRBP(senderFP).addOffsetTo(nextDwarf.getCFAOffset()); - case AMD64ThreadContext.RSP -> getNextRSP().addOffsetTo(nextDwarf.getCFAOffset()); - default -> throw new DebuggerException("Unsupported CFA register: " + nextCFAReg); + // Sender frame is Native + int senderCFAReg = senderDwarf.getCFARegister(); + return switch(senderCFAReg){ + case AMD64ThreadContext.RBP -> senderFP.addOffsetTo(senderDwarf.getCFAOffset()); + case AMD64ThreadContext.RSP -> senderSP.addOffsetTo(senderDwarf.getCFAOffset()); + default -> throw new DebuggerException("Unsupported CFA register: " + senderCFAReg); }; } @Override - public CFrame sender(ThreadProxy th) { - return sender(th, null, null, null); - } - - @Override - public CFrame sender(ThreadProxy th, Address sp, Address fp, Address pc) { - if (dbg.isSignalTrampoline(pc())) { + public CFrame sender(ThreadProxy th, Address senderSP, Address senderFP, Address senderPC) { + if (linuxDbg().isSignalTrampoline(pc())) { // RSP points signal context // https://github.com/torvalds/linux/blob/v6.17/arch/x86/kernel/signal.c#L94 - return getFrameFromReg(dbg, r -> LinuxAMD64ThreadContext.getRegFromSignalTrampoline(this.rsp, r.intValue())); + return getFrameFromReg(linuxDbg(), r -> LinuxAMD64ThreadContext.getRegFromSignalTrampoline(sp(), r.intValue())); } - ThreadContext context = th.getContext(); - - Address nextRSP = sp != null ? sp : getNextRSP(); - if (nextRSP == null) { + senderSP = getSenderSP(senderSP); + if (senderSP == null) { return null; } - Address nextPC = pc != null ? pc : getNextPC(); - if (nextPC == null) { + senderPC = getSenderPC(senderPC); + if (senderPC == null) { return null; } - DwarfParser nextDwarf = null; + DwarfParser senderDwarf = null; boolean fallback = false; try { - nextDwarf = createDwarfParser(nextPC); + senderDwarf = createDwarfParser(linuxDbg(), senderPC); } catch (DebuggerException _) { - // Try again with RIP-1 in case RIP is just outside function bounds, + // Try again with PC-1 in case PC is just outside function bounds, // due to function ending with a `call` instruction. try { - nextDwarf = createDwarfParser(nextPC.addOffsetTo(-1)); + senderDwarf = createDwarfParser(linuxDbg(), senderPC.addOffsetTo(-1)); fallback = true; } catch (DebuggerException _) { // DWARF processing should succeed when the frame is native @@ -213,56 +114,29 @@ public final class LinuxAMD64CFrame extends BasicCFrame { } } - Address nextRBP = getNextRBP(fp); + senderFP = getSenderFP(senderFP); try { - Address nextCFA = getNextCFA(nextDwarf, fp, nextPC); - return isValidFrame(nextCFA, nextRBP) - ? new LinuxAMD64CFrame(dbg, nextRSP, nextRBP, nextCFA, nextPC, nextDwarf, fallback) + Address senderCFA = getSenderCFA(senderDwarf, senderSP, senderFP); + return isValidFrame(senderCFA, senderFP) + ? new LinuxAMD64CFrame(linuxDbg(), senderSP, senderFP, senderCFA, senderPC, senderDwarf, fallback) : null; } catch (DebuggerException e) { - if (dbg.isSignalTrampoline(nextPC)) { - // We can through the caller frame if it is signal trampoline. - // getNextCFA() might fail because DwarfParser cannot find out CFA register. - return new LinuxAMD64CFrame(dbg, nextRSP, nextRBP, null, nextPC, nextDwarf, fallback); + if (linuxDbg().isSignalTrampoline(senderPC)) { + // We can use the caller frame if it is a signal trampoline. + // getSenderCFA() might fail because DwarfParser cannot find out CFA register. + return new LinuxAMD64CFrame(linuxDbg(), senderSP, senderFP, null, senderPC, senderDwarf, fallback); } - // Rethrow the original exception if getNextCFA() failed + // Rethrow the original exception if getSenderCFA() failed // and the caller is not signal trampoline. throw e; } } - private DwarfParser createDwarfParser(Address pc) throws DebuggerException { - DwarfParser nextDwarf = null; - Address libptr = dbg.findLibPtrByAddress(pc); - if (libptr != null) { - try { - nextDwarf = new DwarfParser(libptr); - } catch (DebuggerException _) { - // Bail out to Java frame - } - } - - if (nextDwarf != null) { - nextDwarf.processDwarf(pc); - } - - return nextDwarf; - } - @Override public Frame toFrame() { - return new AMD64Frame(rsp, localVariableBase(), rip); + return new AMD64Frame(sp(), localVariableBase(), pc()); } - // package/class internals only - private static final int ADDRESS_SIZE = 8; - private Address rsp; - private Address rbp; - private Address rip; - private Address cfa; - private LinuxDebugger dbg; - private DwarfParser dwarf; - private boolean use1ByteBeforeToLookup; } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Array.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Array.java index c4eeaf4a367..61067e63707 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Array.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Array.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -83,10 +83,8 @@ public class Array extends Oop { } if (VM.getVM().isCompactObjectHeadersEnabled()) { lengthOffsetInBytes = Oop.getHeaderSize(); - } else if (VM.getVM().isCompressedKlassPointersEnabled()) { - lengthOffsetInBytes = typeSize - VM.getVM().getIntSize(); } else { - lengthOffsetInBytes = typeSize; + lengthOffsetInBytes = typeSize - VM.getVM().getIntSize(); } return lengthOffsetInBytes; } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Instance.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Instance.java index 66efbe3484a..fea4fdaabc2 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Instance.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Instance.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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,10 +57,8 @@ public class Instance extends Oop { public static long getHeaderSize() { if (VM.getVM().isCompactObjectHeadersEnabled()) { return Oop.getHeaderSize(); - } else if (VM.getVM().isCompressedKlassPointersEnabled()) { - return typeSize - VM.getVM().getIntSize(); } else { - return typeSize; + return typeSize - VM.getVM().getIntSize(); } } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Oop.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Oop.java index 75ad4ab1d66..951499974fa 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Oop.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Oop.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -51,8 +51,7 @@ public class Oop { headerSize = markType.getSize(); } else { headerSize = type.getSize(); - klass = new MetadataField(type.getAddressField("_metadata._klass"), 0); - compressedKlass = new NarrowKlassField(type.getAddressField("_metadata._compressed_klass"), 0); + compressedKlass = new NarrowKlassField(type.getAddressField("_compressed_klass"), 0); } } @@ -75,7 +74,6 @@ public class Oop { public static long getHeaderSize() { return headerSize; } // Header size in bytes. private static CIntField mark; - private static MetadataField klass; private static NarrowKlassField compressedKlass; // Accessors for declared fields @@ -83,12 +81,9 @@ public class Oop { public Klass getKlass() { if (VM.getVM().isCompactObjectHeadersEnabled()) { - assert(VM.getVM().isCompressedKlassPointersEnabled()); return getMark().getKlass(); - } else if (VM.getVM().isCompressedKlassPointersEnabled()) { - return (Klass)compressedKlass.getValue(getHandle()); } else { - return (Klass)klass.getValue(getHandle()); + return (Klass)compressedKlass.getValue(getHandle()); } } @@ -157,11 +152,7 @@ public class Oop { if (doVMFields) { visitor.doCInt(mark, true); if (!VM.getVM().isCompactObjectHeadersEnabled()) { - if (VM.getVM().isCompressedKlassPointersEnabled()) { - visitor.doMetadata(compressedKlass, true); - } else { - visitor.doMetadata(klass, true); - } + visitor.doMetadata(compressedKlass, true); } } } @@ -220,10 +211,8 @@ public class Oop { if (VM.getVM().isCompactObjectHeadersEnabled()) { Mark mark = new Mark(handle); return mark.getKlass(); - } else if (VM.getVM().isCompressedKlassPointersEnabled()) { - return (Klass)Metadata.instantiateWrapperFor(handle.getCompKlassAddressAt(compressedKlass.getOffset())); } else { - return (Klass)Metadata.instantiateWrapperFor(handle.getAddressAt(klass.getOffset())); + return (Klass)Metadata.instantiateWrapperFor(handle.getCompKlassAddressAt(compressedKlass.getOffset())); } } }; diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Continuation.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Continuation.java new file mode 100644 index 00000000000..72ba053f451 --- /dev/null +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Continuation.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, NTT DATA. + * 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. + * + */ +package sun.jvm.hotspot.runtime; + +import sun.jvm.hotspot.debugger.Address; + + +public class Continuation { + + public static boolean isReturnBarrierEntry(Address senderPC) { + if (!Continuations.enabled()) { + return false; + } + return VM.getVM().getStubRoutines().contReturnBarrier().equals(senderPC); + } + + public static boolean isSPInContinuation(ContinuationEntry entry, Address sp) { + return entry.getEntrySP().greaterThan(sp); + } + + public static ContinuationEntry getContinuationEntryForSP(JavaThread thread, Address sp) { + ContinuationEntry entry = thread.getContEntry(); + while (entry != null && !isSPInContinuation(entry, sp)) { + entry = entry.getParent(); + } + return entry; + } + + public static Frame continuationBottomSender(JavaThread thread, Frame callee, Address senderSP) { + ContinuationEntry ce = getContinuationEntryForSP(thread, callee.getSP()); + Frame entry = ce.toFrame(); + if (callee.isInterpretedFrame()) { + entry.setSP(senderSP); // sp != unextended_sp + } + return entry; + } + +} diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ContinuationEntry.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ContinuationEntry.java index 73152bdee84..7d8a2ba5993 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ContinuationEntry.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ContinuationEntry.java @@ -1,6 +1,6 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2025, NTT DATA. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, NTT DATA. * 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,17 @@ package sun.jvm.hotspot.runtime; import sun.jvm.hotspot.debugger.*; -import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.runtime.aarch64.*; +import sun.jvm.hotspot.runtime.amd64.*; +import sun.jvm.hotspot.runtime.ppc64.*; +import sun.jvm.hotspot.runtime.riscv64.*; import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; -public class ContinuationEntry extends VMObject { +public abstract class ContinuationEntry extends VMObject { private static long size; + private static AddressField parentField; private static Address returnPC; static { @@ -41,13 +46,28 @@ public class ContinuationEntry extends VMObject { private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { Type type = db.lookupType("ContinuationEntry"); size = type.getSize(); + parentField = type.getAddressField("_parent"); returnPC = type.getAddressField("_return_pc").getValue(); } + public static ContinuationEntry create(Address addr) { + return switch (VM.getVM().getDebugger().getCPU()) { + case "amd64" -> VMObjectFactory.newObject(AMD64ContinuationEntry.class, addr); + case "aarch64" -> VMObjectFactory.newObject(AARCH64ContinuationEntry.class, addr); + case "riscv64" -> VMObjectFactory.newObject(RISCV64ContinuationEntry.class, addr); + case "ppc64" -> VMObjectFactory.newObject(PPC64ContinuationEntry.class, addr); + default -> throw new UnsupportedPlatformException("Continuation is not yet implemented."); + }; + } + public ContinuationEntry(Address addr) { super(addr); } + public ContinuationEntry getParent() { + return create(parentField.getValue(addr)); + } + public Address getEntryPC() { return returnPC; } @@ -60,4 +80,6 @@ public class ContinuationEntry extends VMObject { return this.getAddress().addOffsetTo(size); } + public abstract Frame toFrame(); + } diff --git a/src/hotspot/share/cds/aotGrowableArray.cpp b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Continuations.java similarity index 78% rename from src/hotspot/share/cds/aotGrowableArray.cpp rename to src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Continuations.java index ec63e7aa57f..884f8764ba5 100644 --- a/src/hotspot/share/cds/aotGrowableArray.cpp +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Continuations.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, NTT DATA. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,14 +22,12 @@ * questions. * */ +package sun.jvm.hotspot.runtime; -#include "cds/aotGrowableArray.hpp" -#include "cds/aotMetaspace.hpp" -#include "memory/allocation.inline.hpp" -#include "utilities/growableArray.hpp" +public class Continuations { + + public static boolean enabled() { + return VM.getVM().getCommandLineFlag("VMContinuations").getBool(); + } -void AOTGrowableArrayHelper::deallocate(void* mem) { - if (!AOTMetaspace::in_aot_cache(mem)) { - GrowableArrayCHeapAllocator::deallocate(mem); - } } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Frame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Frame.java index ee9e0ecdafd..978fb39ad1c 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Frame.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Frame.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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,6 +138,7 @@ public abstract class Frame implements Cloneable { } public abstract Address getSP(); + public abstract void setSP(Address newSP); public abstract Address getID(); public abstract Address getFP(); diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java index 826b5cecfd5..c18bcf8cd37 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -343,7 +343,7 @@ public class JavaThread extends Thread { } public ContinuationEntry getContEntry() { - return VMObjectFactory.newObject(ContinuationEntry.class, contEntryField.getValue(addr)); + return ContinuationEntry.create(contEntryField.getValue(addr)); } /** Gets the Java-side thread object for this JavaThread */ diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/StubRoutines.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/StubRoutines.java index 38a3103ac50..85d8c8cd3b6 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/StubRoutines.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/StubRoutines.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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,6 +34,7 @@ import sun.jvm.hotspot.utilities.Observer; public class StubRoutines { private static AddressField callStubReturnAddressField; + private static AddressField contReturnBarrierField; static { VM.registerVMInitializedObserver(new Observer() { @@ -46,6 +47,7 @@ public class StubRoutines { private static synchronized void initialize(TypeDataBase db) { Type type = db.lookupType("StubRoutines"); callStubReturnAddressField = type.getAddressField("_call_stub_return_address"); + contReturnBarrierField = type.getAddressField("_cont_returnBarrier"); } public StubRoutines() { @@ -59,4 +61,9 @@ public class StubRoutines { return (addr.equals(returnPC)); } } + + public Address contReturnBarrier() { + return contReturnBarrierField.getValue(); + } + } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/VM.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/VM.java index 1607563150a..2ec96121934 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/VM.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/VM.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -144,7 +144,6 @@ public class VM { private static CIntegerType boolType; private Boolean sharingEnabled; private Boolean compressedOopsEnabled; - private Boolean compressedKlassPointersEnabled; private Boolean compactObjectHeadersEnabled; // command line flags supplied to VM - see struct JVMFlag in jvmFlag.hpp @@ -515,11 +514,7 @@ public class VM { heapOopSize = (int)getOopSize(); } - if (isCompressedKlassPointersEnabled()) { - klassPtrSize = (int)getIntSize(); - } else { - klassPtrSize = (int)getOopSize(); // same as an oop - } + klassPtrSize = (int)getIntSize(); } /** This could be used by a reflective runtime system */ @@ -938,15 +933,6 @@ public class VM { return compressedOopsEnabled.booleanValue(); } - public boolean isCompressedKlassPointersEnabled() { - if (compressedKlassPointersEnabled == null) { - Flag flag = getCommandLineFlag("UseCompressedClassPointers"); - compressedKlassPointersEnabled = (flag == null) ? Boolean.FALSE: - (flag.getBool()? Boolean.TRUE: Boolean.FALSE); - } - return compressedKlassPointersEnabled.booleanValue(); - } - public boolean isCompactObjectHeadersEnabled() { if (compactObjectHeadersEnabled == null) { Flag flag = getCommandLineFlag("UseCompactObjectHeaders"); diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/aarch64/AARCH64ContinuationEntry.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/aarch64/AARCH64ContinuationEntry.java new file mode 100644 index 00000000000..b373167a37c --- /dev/null +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/aarch64/AARCH64ContinuationEntry.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, NTT DATA. + * 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. + * + */ +package sun.jvm.hotspot.runtime.aarch64; + +import sun.jvm.hotspot.debugger.Address; +import sun.jvm.hotspot.runtime.ContinuationEntry; +import sun.jvm.hotspot.runtime.Frame; + + +public class AARCH64ContinuationEntry extends ContinuationEntry { + + public AARCH64ContinuationEntry(Address addr) { + super(addr); + } + + @Override + public Frame toFrame() { + return new AARCH64Frame(getEntrySP(), getEntrySP(), getEntryFP(), getEntryPC()); + } + +} diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/aarch64/AARCH64Frame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/aarch64/AARCH64Frame.java index 7233d508cbc..5e73150c6cf 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/aarch64/AARCH64Frame.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/aarch64/AARCH64Frame.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2015, 2019, Red Hat Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -206,6 +206,11 @@ public class AARCH64Frame extends Frame { public Address getSP() { return raw_sp; } public Address getID() { return raw_sp; } + @Override + public void setSP(Address newSP) { + raw_sp = newSP; + } + // FIXME: not implemented yet public boolean isSignalHandlerFrameDbg() { return false; } public int getSignalNumberDbg() { return 0; } @@ -360,16 +365,6 @@ public class AARCH64Frame extends Frame { map.setLocation(fp, savedFPAddr); } - private Frame senderForContinuationStub(AARCH64RegisterMap map, CodeBlob cb) { - var contEntry = map.getThread().getContEntry(); - - Address senderSP = contEntry.getEntrySP(); - Address senderPC = contEntry.getEntryPC(); - Address senderFP = contEntry.getEntryFP(); - - return new AARCH64Frame(senderSP, senderFP, senderPC); - } - private Frame senderForCompiledFrame(AARCH64RegisterMap map, CodeBlob cb) { if (DEBUG) { System.out.println("senderForCompiledFrame"); @@ -416,6 +411,22 @@ public class AARCH64Frame extends Frame { updateMapWithSavedLink(map, savedFPAddr); } + if (Continuation.isReturnBarrierEntry(senderPC)) { + // We assume WalkContinuation is "WalkContinuation::skip". + // It is same with c'tor arguments of RegisterMap in frame::next_frame(). + // + // HotSpot code in cpu/aarch64/frame_aarch64.inline.hpp: + // + // if (Continuation::is_return_barrier_entry(sender_pc)) { + // if (map->walk_cont()) { // about to walk into an h-stack + // return Continuation::top_frame(*this, map); + // } else { + // return Continuation::continuation_bottom_sender(map->thread(), *this, l_sender_sp); + // } + // } + return Continuation.continuationBottomSender(map.getThread(), this, senderSP); + } + return new AARCH64Frame(senderSP, savedFPAddr.getAddressAt(0), senderPC); } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/amd64/AMD64ContinuationEntry.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/amd64/AMD64ContinuationEntry.java new file mode 100644 index 00000000000..3cbebfce2f4 --- /dev/null +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/amd64/AMD64ContinuationEntry.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, NTT DATA. + * 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. + * + */ +package sun.jvm.hotspot.runtime.amd64; + +import sun.jvm.hotspot.debugger.Address; +import sun.jvm.hotspot.runtime.ContinuationEntry; +import sun.jvm.hotspot.runtime.Frame; + + +public class AMD64ContinuationEntry extends ContinuationEntry { + + public AMD64ContinuationEntry(Address addr) { + super(addr); + } + + @Override + public Frame toFrame() { + return new AMD64Frame(getEntrySP(), getEntrySP(), getEntryFP(), getEntryPC()); + } + +} diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/amd64/AMD64Frame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/amd64/AMD64Frame.java index fa9d50160e1..2b78157e2b2 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/amd64/AMD64Frame.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/amd64/AMD64Frame.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, 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 @@ -206,6 +206,11 @@ public class AMD64Frame extends Frame { public Address getSP() { return raw_sp; } public Address getID() { return raw_sp; } + @Override + public void setSP(Address newSP) { + raw_sp = newSP; + } + // FIXME: not implemented yet (should be done for Solaris) public boolean isSignalHandlerFrameDbg() { return false; } public int getSignalNumberDbg() { return 0; } @@ -258,6 +263,23 @@ public class AMD64Frame extends Frame { // update it accordingly map.setIncludeArgumentOops(false); + // HotSpot has following code in frame::sender_raw() at frame_x86.inline.hpp, however + // in_cont() should be false. + // + // if (map->in_cont()) { // already in an h-stack + // return map->stack_chunk()->sender(*this, map); + // } + // + // in_cont() returns true if _chunk() is not null. + // + // frame::next_frame() creates RegisterMap instance with 4 arguments. + // It sets RegisterMap::WalkContinuation::skip to final argument (walk_cont), + // therefore _chunk will not be initialized by the following code in c'tor of RegisterMap. + // + // if (walk_cont == WalkContinuation::include && thread != nullptr && thread->last_continuation() != nullptr) { + // _chunk = stackChunkHandle(Thread::current()->handle_area()->allocate_null_handle(), true /* dummy */); + // } + if (isEntryFrame()) return senderForEntryFrame(map); if (isInterpretedFrame()) return senderForInterpreterFrame(map); @@ -360,16 +382,6 @@ public class AMD64Frame extends Frame { map.setLocation(rbp, savedFPAddr); } - private Frame senderForContinuationStub(AMD64RegisterMap map, CodeBlob cb) { - var contEntry = map.getThread().getContEntry(); - - Address senderSP = contEntry.getEntrySP(); - Address senderPC = contEntry.getEntryPC(); - Address senderFP = contEntry.getEntryFP(); - - return new AMD64Frame(senderSP, senderFP, senderPC); - } - private Frame senderForCompiledFrame(AMD64RegisterMap map, CodeBlob cb) { if (DEBUG) { System.out.println("senderForCompiledFrame"); @@ -408,6 +420,22 @@ public class AMD64Frame extends Frame { updateMapWithSavedLink(map, savedFPAddr); } + if (Continuation.isReturnBarrierEntry(senderPC)) { + // We assume WalkContinuation is "WalkContinuation::skip". + // It is same with c'tor arguments of RegisterMap in frame::next_frame(). + // + // HotSpot code in cpu/x86/frame_x86.inline.hpp: + // + // if (Continuation::is_return_barrier_entry(sender_pc)) { + // if (map->walk_cont()) { // about to walk into an h-stack + // return Continuation::top_frame(*this, map); + // } else { + // return Continuation::continuation_bottom_sender(map->thread(), *this, sender_sp); + // } + // } + return Continuation.continuationBottomSender(map.getThread(), this, senderSP); + } + return new AMD64Frame(senderSP, savedFPAddr.getAddressAt(0), senderPC); } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ppc64/PPC64ContinuationEntry.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ppc64/PPC64ContinuationEntry.java new file mode 100644 index 00000000000..fac71cc9953 --- /dev/null +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ppc64/PPC64ContinuationEntry.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, NTT DATA. + * 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. + * + */ +package sun.jvm.hotspot.runtime.ppc64; + +import sun.jvm.hotspot.debugger.Address; +import sun.jvm.hotspot.runtime.ContinuationEntry; +import sun.jvm.hotspot.runtime.Frame; + + +public class PPC64ContinuationEntry extends ContinuationEntry { + + public PPC64ContinuationEntry(Address addr) { + super(addr); + } + + @Override + public Frame toFrame() { + return new PPC64Frame(getEntrySP(), getEntrySP(), getEntryFP(), getEntryPC()); + } + +} diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ppc64/PPC64Frame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ppc64/PPC64Frame.java index cae034c9613..a663d016011 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ppc64/PPC64Frame.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ppc64/PPC64Frame.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -198,6 +198,11 @@ public class PPC64Frame extends Frame { public Address getSP() { return raw_sp; } public Address getID() { return raw_sp; } + @Override + public void setSP(Address newSP) { + raw_sp = newSP; + } + // FIXME: not implemented yet (should be done for Solaris/PPC64) public boolean isSignalHandlerFrameDbg() { return false; } public int getSignalNumberDbg() { return 0; } @@ -260,9 +265,7 @@ public class PPC64Frame extends Frame { if (cb != null) { if (cb.isUpcallStub()) { return senderForUpcallStub(map, (UpcallStub)cb); - } else if (cb.isContinuationStub()) { - return senderForContinuationStub(map, cb); - } else { + } else if (cb.getFrameSize() > 0) { return senderForCompiledFrame(map, cb); } } @@ -337,16 +340,6 @@ public class PPC64Frame extends Frame { return new PPC64Frame(sp, unextendedSP, getLink(), getSenderPC()); } - private Frame senderForContinuationStub(PPC64RegisterMap map, CodeBlob cb) { - var contEntry = map.getThread().getContEntry(); - - Address sp = contEntry.getEntrySP(); - Address pc = contEntry.getEntryPC(); - Address fp = contEntry.getEntryFP(); - - return new PPC64Frame(sp, fp, pc); - } - private Frame senderForCompiledFrame(PPC64RegisterMap map, CodeBlob cb) { if (DEBUG) { System.out.println("senderForCompiledFrame"); @@ -379,6 +372,22 @@ public class PPC64Frame extends Frame { } } + if (Continuation.isReturnBarrierEntry(senderPC)) { + // We assume WalkContinuation is "WalkContinuation::skip". + // It is same with c'tor arguments of RegisterMap in frame::next_frame(). + // + // HotSpot code in cpu/ppc/frame_ppc.inline.hpp: + // + // if (Continuation::is_return_barrier_entry(sender_pc)) { + // if (map->walk_cont()) { // about to walk into an h-stack + // return Continuation::top_frame(*this, map); + // } else { + // return Continuation::continuation_bottom_sender(map->thread(), *this, l_sender_sp); + // } + // } + return Continuation.continuationBottomSender(map.getThread(), this, senderSP); + } + return new PPC64Frame(senderSP, getLink(), senderPC); } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/riscv64/RISCV64ContinuationEntry.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/riscv64/RISCV64ContinuationEntry.java new file mode 100644 index 00000000000..ec04498a6c0 --- /dev/null +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/riscv64/RISCV64ContinuationEntry.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, NTT DATA. + * 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. + * + */ +package sun.jvm.hotspot.runtime.riscv64; + +import sun.jvm.hotspot.debugger.Address; +import sun.jvm.hotspot.runtime.ContinuationEntry; +import sun.jvm.hotspot.runtime.Frame; + + +public class RISCV64ContinuationEntry extends ContinuationEntry { + + public RISCV64ContinuationEntry(Address addr) { + super(addr); + } + + @Override + public Frame toFrame() { + return new RISCV64Frame(getEntrySP(), getEntrySP(), getEntryFP(), getEntryPC()); + } + +} diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/riscv64/RISCV64Frame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/riscv64/RISCV64Frame.java index 44c8f4c679c..a35c0735979 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/riscv64/RISCV64Frame.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/riscv64/RISCV64Frame.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2015, 2019, Red Hat Inc. * Copyright (c) 2021, 2023, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -201,6 +201,11 @@ public class RISCV64Frame extends Frame { public Address getSP() { return raw_sp; } public Address getID() { return raw_sp; } + @Override + public void setSP(Address newSP) { + raw_sp = newSP; + } + // FIXME: not implemented yet public boolean isSignalHandlerFrameDbg() { return false; } public int getSignalNumberDbg() { return 0; } @@ -264,9 +269,7 @@ public class RISCV64Frame extends Frame { if (cb != null) { if (cb.isUpcallStub()) { return senderForUpcallStub(map, (UpcallStub)cb); - } else if (cb.isContinuationStub()) { - return senderForContinuationStub(map, cb); - } else { + } else if (cb.getFrameSize() > 0) { return senderForCompiledFrame(map, cb); } } @@ -354,16 +357,6 @@ public class RISCV64Frame extends Frame { map.setLocation(fp, savedFPAddr); } - private Frame senderForContinuationStub(RISCV64RegisterMap map, CodeBlob cb) { - var contEntry = map.getThread().getContEntry(); - - Address senderSP = contEntry.getEntrySP(); - Address senderPC = contEntry.getEntryPC(); - Address senderFP = contEntry.getEntryFP(); - - return new RISCV64Frame(senderSP, senderFP, senderPC); - } - private Frame senderForCompiledFrame(RISCV64RegisterMap map, CodeBlob cb) { if (DEBUG) { System.out.println("senderForCompiledFrame"); @@ -406,6 +399,22 @@ public class RISCV64Frame extends Frame { updateMapWithSavedLink(map, savedFPAddr); } + if (Continuation.isReturnBarrierEntry(senderPC)) { + // We assume WalkContinuation is "WalkContinuation::skip". + // It is same with c'tor arguments of RegisterMap in frame::next_frame(). + // + // HotSpot code in cpu/riscv/frame_riscv.inline.hpp: + // + // if (Continuation::is_return_barrier_entry(sender_pc)) { + // if (map->walk_cont()) { // about to walk into an h-stack + // return Continuation::top_frame(*this, map); + // } else { + // return Continuation::continuation_bottom_sender(map->thread(), *this, l_sender_sp); + // } + // } + return Continuation.continuationBottomSender(map.getThread(), this, senderSP); + } + return new RISCV64Frame(senderSP, savedFPAddr.getAddressAt(0), senderPC); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractMask.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractMask.java index 5b762edfd3b..9ac90c08c27 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractMask.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractMask.java @@ -24,17 +24,19 @@ */ package jdk.incubator.vector; -import java.util.Objects; - -import jdk.internal.vm.annotation.ForceInline; - import jdk.internal.misc.Unsafe; - +import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; import static jdk.incubator.vector.VectorOperators.*; -abstract class AbstractMask extends VectorMask { +abstract sealed class AbstractMask extends VectorMask + permits ByteVector64.ByteMask64, ByteVector128.ByteMask128, ByteVector256.ByteMask256, ByteVector512.ByteMask512, ByteVectorMax.ByteMaskMax, + DoubleVector64.DoubleMask64, DoubleVector128.DoubleMask128, DoubleVector256.DoubleMask256, DoubleVector512.DoubleMask512, DoubleVectorMax.DoubleMaskMax, + FloatVector64.FloatMask64, FloatVector128.FloatMask128, FloatVector256.FloatMask256, FloatVector512.FloatMask512, FloatVectorMax.FloatMaskMax, + IntVector64.IntMask64, IntVector128.IntMask128, IntVector256.IntMask256, IntVector512.IntMask512, IntVectorMax.IntMaskMax, + LongVector64.LongMask64, LongVector128.LongMask128, LongVector256.LongMask256, LongVector512.LongMask512, LongVectorMax.LongMaskMax, + ShortVector64.ShortMask64, ShortVector128.ShortMask128, ShortVector256.ShortMask256, ShortVector512.ShortMask512, ShortVectorMax.ShortMaskMax { AbstractMask(boolean[] bits) { super(bits); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractShuffle.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractShuffle.java index 075400a0d4a..bea495f74fc 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractShuffle.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractShuffle.java @@ -25,10 +25,17 @@ package jdk.incubator.vector; import java.util.function.IntUnaryOperator; + import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -abstract class AbstractShuffle extends VectorShuffle { +abstract sealed class AbstractShuffle extends VectorShuffle + permits ByteVector64.ByteShuffle64, ByteVector128.ByteShuffle128, ByteVector256.ByteShuffle256, ByteVector512.ByteShuffle512, ByteVectorMax.ByteShuffleMax, + DoubleVector64.DoubleShuffle64, DoubleVector128.DoubleShuffle128, DoubleVector256.DoubleShuffle256, DoubleVector512.DoubleShuffle512, DoubleVectorMax.DoubleShuffleMax, + FloatVector64.FloatShuffle64, FloatVector128.FloatShuffle128, FloatVector256.FloatShuffle256, FloatVector512.FloatShuffle512, FloatVectorMax.FloatShuffleMax, + IntVector64.IntShuffle64, IntVector128.IntShuffle128, IntVector256.IntShuffle256, IntVector512.IntShuffle512, IntVectorMax.IntShuffleMax, + LongVector64.LongShuffle64, LongVector128.LongShuffle128, LongVector256.LongShuffle256, LongVector512.LongShuffle512, LongVectorMax.LongShuffleMax, + ShortVector64.ShortShuffle64, ShortVector128.ShortShuffle128, ShortVector256.ShortShuffle256, ShortVector512.ShortShuffle512, ShortVectorMax.ShortShuffleMax { static final IntUnaryOperator IDENTITY = i -> i; // Internal representation allows for a maximum index of E.MAX_VALUE - 1 diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractSpecies.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractSpecies.java index 6c834077387..3fd2be34346 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractSpecies.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractSpecies.java @@ -24,39 +24,31 @@ */ package jdk.incubator.vector; -import java.lang.foreign.MemorySegment; -import jdk.internal.vm.annotation.ForceInline; -import jdk.internal.vm.annotation.Stable; import java.lang.reflect.Array; -import java.nio.ByteOrder; import java.util.Arrays; import java.util.function.Function; import java.util.function.IntUnaryOperator; -abstract class AbstractSpecies extends jdk.internal.vm.vector.VectorSupport.VectorSpecies - implements VectorSpecies { - @Stable +import jdk.internal.vm.annotation.ForceInline; +import jdk.internal.vm.annotation.Stable; +import jdk.internal.vm.annotation.TrustFinalFields; + +@TrustFinalFields +abstract sealed class AbstractSpecies extends jdk.internal.vm.vector.VectorSupport.VectorSpecies + implements VectorSpecies + permits ByteVector.ByteSpecies, DoubleVector.DoubleSpecies, FloatVector.FloatSpecies, + IntVector.IntSpecies, LongVector.LongSpecies, ShortVector.ShortSpecies { final VectorShape vectorShape; - @Stable final LaneType laneType; - @Stable final int laneCount; - @Stable final int laneCountLog2P1; - @Stable final Class> vectorType; - @Stable final Class> maskType; - @Stable final Class> shuffleType; - @Stable final Function> vectorFactory; - @Stable final VectorShape indexShape; - @Stable final int maxScale, minScale; - @Stable final int vectorBitSize, vectorByteSize; AbstractSpecies(VectorShape vectorShape, diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractVector.java index 80260c2bd30..ea8112cc2ae 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractVector.java @@ -25,22 +25,17 @@ package jdk.incubator.vector; import java.lang.foreign.MemorySegment; +import java.nio.ByteOrder; +import java.util.function.IntUnaryOperator; -import jdk.internal.foreign.AbstractMemorySegmentImpl; -import jdk.internal.foreign.Utils; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import java.lang.foreign.ValueLayout; -import java.lang.reflect.Array; -import java.nio.ByteOrder; -import java.util.Objects; -import java.util.function.IntUnaryOperator; - import static jdk.incubator.vector.VectorOperators.*; @SuppressWarnings("cast") -abstract class AbstractVector extends Vector { +abstract sealed class AbstractVector extends Vector + permits ByteVector, DoubleVector, FloatVector, IntVector, LongVector, ShortVector { /** * The order of vector bytes when stored in natural, * array elements of the same lane type. diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector.java index 846032cb5c6..7231ada3273 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector.java @@ -49,7 +49,8 @@ import static jdk.incubator.vector.VectorOperators.*; * {@code byte} values. */ @SuppressWarnings("cast") // warning: redundant cast -public abstract class ByteVector extends AbstractVector { +public abstract sealed class ByteVector extends AbstractVector + permits ByteVector64, ByteVector128, ByteVector256, ByteVector512, ByteVectorMax { ByteVector(byte[] vec) { super(vec); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector128.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector128.java index c38e8d0f8a0..36ea8d081a8 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector128.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector128.java @@ -25,22 +25,22 @@ package jdk.incubator.vector; import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class ByteVector128 extends ByteVector { static final ByteSpecies VSPECIES = (ByteSpecies) ByteVector.SPECIES_128; @@ -371,7 +371,7 @@ final class ByteVector128 extends ByteVector { @Override @ForceInline public final ByteShuffle128 toShuffle() { - return (ByteShuffle128) toShuffle(vspecies(), false); + return (ByteShuffle128) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -598,7 +598,7 @@ final class ByteVector128 extends ByteVector { } // Mask - + @ValueBased static final class ByteMask128 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -646,7 +646,7 @@ final class ByteVector128 extends ByteVector { @Override ByteMask128 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -656,7 +656,7 @@ final class ByteVector128 extends ByteVector { @Override ByteMask128 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((ByteMask128)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -806,16 +806,16 @@ final class ByteVector128 extends ByteVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, ByteMask128.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((ByteMask128)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((ByteMask128)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, ByteMask128.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((ByteMask128)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((ByteMask128)m).getBits())); } @ForceInline @@ -823,7 +823,7 @@ final class ByteVector128 extends ByteVector { static ByteMask128 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(ByteMask128.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final ByteMask128 TRUE_MASK = new ByteMask128(true); private static final ByteMask128 FALSE_MASK = new ByteMask128(false); @@ -831,7 +831,7 @@ final class ByteVector128 extends ByteVector { } // Shuffle - + @ValueBased static final class ByteShuffle128 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -883,7 +883,7 @@ final class ByteVector128 extends ByteVector { @Override ByteVector128 toBitsVector0() { - return ((ByteVector128) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((ByteVector128) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -934,7 +934,7 @@ final class ByteVector128 extends ByteVector { @ForceInline public final ByteMask128 laneIsValid() { return (ByteMask128) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -942,7 +942,7 @@ final class ByteVector128 extends ByteVector { public final ByteShuffle128 rearrange(VectorShuffle shuffle) { ByteShuffle128 concreteShuffle = (ByteShuffle128) shuffle; return (ByteShuffle128) toBitsVector().rearrange(concreteShuffle) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -955,7 +955,7 @@ final class ByteVector128 extends ByteVector { v = (ByteVector128) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (ByteShuffle128) v.toShuffle(vspecies(), false); + return (ByteShuffle128) v.toShuffle(VSPECIES, false); } private static byte[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector256.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector256.java index 0eec0c56e37..a11268ea40d 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector256.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector256.java @@ -25,22 +25,22 @@ package jdk.incubator.vector; import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class ByteVector256 extends ByteVector { static final ByteSpecies VSPECIES = (ByteSpecies) ByteVector.SPECIES_256; @@ -371,7 +371,7 @@ final class ByteVector256 extends ByteVector { @Override @ForceInline public final ByteShuffle256 toShuffle() { - return (ByteShuffle256) toShuffle(vspecies(), false); + return (ByteShuffle256) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -630,7 +630,7 @@ final class ByteVector256 extends ByteVector { } // Mask - + @ValueBased static final class ByteMask256 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -678,7 +678,7 @@ final class ByteVector256 extends ByteVector { @Override ByteMask256 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -688,7 +688,7 @@ final class ByteVector256 extends ByteVector { @Override ByteMask256 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((ByteMask256)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -838,16 +838,16 @@ final class ByteVector256 extends ByteVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, ByteMask256.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((ByteMask256)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((ByteMask256)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, ByteMask256.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((ByteMask256)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((ByteMask256)m).getBits())); } @ForceInline @@ -855,7 +855,7 @@ final class ByteVector256 extends ByteVector { static ByteMask256 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(ByteMask256.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final ByteMask256 TRUE_MASK = new ByteMask256(true); private static final ByteMask256 FALSE_MASK = new ByteMask256(false); @@ -863,7 +863,7 @@ final class ByteVector256 extends ByteVector { } // Shuffle - + @ValueBased static final class ByteShuffle256 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -915,7 +915,7 @@ final class ByteVector256 extends ByteVector { @Override ByteVector256 toBitsVector0() { - return ((ByteVector256) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((ByteVector256) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -966,7 +966,7 @@ final class ByteVector256 extends ByteVector { @ForceInline public final ByteMask256 laneIsValid() { return (ByteMask256) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -974,7 +974,7 @@ final class ByteVector256 extends ByteVector { public final ByteShuffle256 rearrange(VectorShuffle shuffle) { ByteShuffle256 concreteShuffle = (ByteShuffle256) shuffle; return (ByteShuffle256) toBitsVector().rearrange(concreteShuffle) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -987,7 +987,7 @@ final class ByteVector256 extends ByteVector { v = (ByteVector256) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (ByteShuffle256) v.toShuffle(vspecies(), false); + return (ByteShuffle256) v.toShuffle(VSPECIES, false); } private static byte[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector512.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector512.java index 138319b60d4..707254e034e 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector512.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector512.java @@ -25,22 +25,22 @@ package jdk.incubator.vector; import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class ByteVector512 extends ByteVector { static final ByteSpecies VSPECIES = (ByteSpecies) ByteVector.SPECIES_512; @@ -371,7 +371,7 @@ final class ByteVector512 extends ByteVector { @Override @ForceInline public final ByteShuffle512 toShuffle() { - return (ByteShuffle512) toShuffle(vspecies(), false); + return (ByteShuffle512) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -694,7 +694,7 @@ final class ByteVector512 extends ByteVector { } // Mask - + @ValueBased static final class ByteMask512 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -742,7 +742,7 @@ final class ByteVector512 extends ByteVector { @Override ByteMask512 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -752,7 +752,7 @@ final class ByteVector512 extends ByteVector { @Override ByteMask512 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((ByteMask512)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -902,16 +902,16 @@ final class ByteVector512 extends ByteVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, ByteMask512.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((ByteMask512)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((ByteMask512)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, ByteMask512.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((ByteMask512)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((ByteMask512)m).getBits())); } @ForceInline @@ -919,7 +919,7 @@ final class ByteVector512 extends ByteVector { static ByteMask512 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(ByteMask512.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final ByteMask512 TRUE_MASK = new ByteMask512(true); private static final ByteMask512 FALSE_MASK = new ByteMask512(false); @@ -927,7 +927,7 @@ final class ByteVector512 extends ByteVector { } // Shuffle - + @ValueBased static final class ByteShuffle512 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -979,7 +979,7 @@ final class ByteVector512 extends ByteVector { @Override ByteVector512 toBitsVector0() { - return ((ByteVector512) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((ByteVector512) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -1030,7 +1030,7 @@ final class ByteVector512 extends ByteVector { @ForceInline public final ByteMask512 laneIsValid() { return (ByteMask512) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -1038,7 +1038,7 @@ final class ByteVector512 extends ByteVector { public final ByteShuffle512 rearrange(VectorShuffle shuffle) { ByteShuffle512 concreteShuffle = (ByteShuffle512) shuffle; return (ByteShuffle512) toBitsVector().rearrange(concreteShuffle) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -1051,7 +1051,7 @@ final class ByteVector512 extends ByteVector { v = (ByteVector512) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (ByteShuffle512) v.toShuffle(vspecies(), false); + return (ByteShuffle512) v.toShuffle(VSPECIES, false); } private static byte[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector64.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector64.java index d7c7c78534b..d304edfc0c7 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector64.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector64.java @@ -25,22 +25,22 @@ package jdk.incubator.vector; import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class ByteVector64 extends ByteVector { static final ByteSpecies VSPECIES = (ByteSpecies) ByteVector.SPECIES_64; @@ -371,7 +371,7 @@ final class ByteVector64 extends ByteVector { @Override @ForceInline public final ByteShuffle64 toShuffle() { - return (ByteShuffle64) toShuffle(vspecies(), false); + return (ByteShuffle64) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -582,7 +582,7 @@ final class ByteVector64 extends ByteVector { } // Mask - + @ValueBased static final class ByteMask64 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -630,7 +630,7 @@ final class ByteVector64 extends ByteVector { @Override ByteMask64 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -640,7 +640,7 @@ final class ByteVector64 extends ByteVector { @Override ByteMask64 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((ByteMask64)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -790,16 +790,16 @@ final class ByteVector64 extends ByteVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, ByteMask64.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((ByteMask64)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((ByteMask64)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, ByteMask64.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((ByteMask64)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((ByteMask64)m).getBits())); } @ForceInline @@ -807,7 +807,7 @@ final class ByteVector64 extends ByteVector { static ByteMask64 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(ByteMask64.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final ByteMask64 TRUE_MASK = new ByteMask64(true); private static final ByteMask64 FALSE_MASK = new ByteMask64(false); @@ -815,7 +815,7 @@ final class ByteVector64 extends ByteVector { } // Shuffle - + @ValueBased static final class ByteShuffle64 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -867,7 +867,7 @@ final class ByteVector64 extends ByteVector { @Override ByteVector64 toBitsVector0() { - return ((ByteVector64) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((ByteVector64) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -918,7 +918,7 @@ final class ByteVector64 extends ByteVector { @ForceInline public final ByteMask64 laneIsValid() { return (ByteMask64) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -926,7 +926,7 @@ final class ByteVector64 extends ByteVector { public final ByteShuffle64 rearrange(VectorShuffle shuffle) { ByteShuffle64 concreteShuffle = (ByteShuffle64) shuffle; return (ByteShuffle64) toBitsVector().rearrange(concreteShuffle) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -939,7 +939,7 @@ final class ByteVector64 extends ByteVector { v = (ByteVector64) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (ByteShuffle64) v.toShuffle(vspecies(), false); + return (ByteShuffle64) v.toShuffle(VSPECIES, false); } private static byte[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVectorMax.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVectorMax.java index 636aa83893a..0084995346b 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVectorMax.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVectorMax.java @@ -25,22 +25,22 @@ package jdk.incubator.vector; import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class ByteVectorMax extends ByteVector { static final ByteSpecies VSPECIES = (ByteSpecies) ByteVector.SPECIES_MAX; @@ -371,7 +371,7 @@ final class ByteVectorMax extends ByteVector { @Override @ForceInline public final ByteShuffleMax toShuffle() { - return (ByteShuffleMax) toShuffle(vspecies(), false); + return (ByteShuffleMax) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -568,7 +568,7 @@ final class ByteVectorMax extends ByteVector { } // Mask - + @ValueBased static final class ByteMaskMax extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -616,7 +616,7 @@ final class ByteVectorMax extends ByteVector { @Override ByteMaskMax uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -626,7 +626,7 @@ final class ByteVectorMax extends ByteVector { @Override ByteMaskMax bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((ByteMaskMax)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -776,16 +776,16 @@ final class ByteVectorMax extends ByteVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, ByteMaskMax.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((ByteMaskMax)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((ByteMaskMax)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, ByteMaskMax.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((ByteMaskMax)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((ByteMaskMax)m).getBits())); } @ForceInline @@ -793,7 +793,7 @@ final class ByteVectorMax extends ByteVector { static ByteMaskMax maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(ByteMaskMax.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final ByteMaskMax TRUE_MASK = new ByteMaskMax(true); private static final ByteMaskMax FALSE_MASK = new ByteMaskMax(false); @@ -801,7 +801,7 @@ final class ByteVectorMax extends ByteVector { } // Shuffle - + @ValueBased static final class ByteShuffleMax extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -853,7 +853,7 @@ final class ByteVectorMax extends ByteVector { @Override ByteVectorMax toBitsVector0() { - return ((ByteVectorMax) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((ByteVectorMax) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -904,7 +904,7 @@ final class ByteVectorMax extends ByteVector { @ForceInline public final ByteMaskMax laneIsValid() { return (ByteMaskMax) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -912,7 +912,7 @@ final class ByteVectorMax extends ByteVector { public final ByteShuffleMax rearrange(VectorShuffle shuffle) { ByteShuffleMax concreteShuffle = (ByteShuffleMax) shuffle; return (ByteShuffleMax) toBitsVector().rearrange(concreteShuffle) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -925,7 +925,7 @@ final class ByteVectorMax extends ByteVector { v = (ByteVectorMax) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (ByteShuffleMax) v.toShuffle(vspecies(), false); + return (ByteShuffleMax) v.toShuffle(VSPECIES, false); } private static byte[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/CPUFeatures.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/CPUFeatures.java index c0d8ef03ada..05b5f6f69f4 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/CPUFeatures.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/CPUFeatures.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, 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 @@ -36,7 +36,7 @@ import static jdk.internal.vm.vector.Utils.debug; /** * Enumerates CPU ISA extensions supported by the JVM on the current hardware. */ -/*package-private*/ class CPUFeatures { +/*package-private*/ final class CPUFeatures { private static final Set features = getCPUFeatures(); private static Set getCPUFeatures() { @@ -74,9 +74,6 @@ import static jdk.internal.vm.vector.Utils.debug; debug("AVX=%b; AVX2=%b; AVX512F=%b; AVX512DQ=%b", SUPPORTS_AVX, SUPPORTS_AVX2, SUPPORTS_AVX512F, SUPPORTS_AVX512DQ); - assert SUPPORTS_AVX512F == (VectorShape.getMaxVectorBitSize(int.class) == 512); - assert SUPPORTS_AVX2 == (VectorShape.getMaxVectorBitSize(byte.class) >= 256); - assert SUPPORTS_AVX == (VectorShape.getMaxVectorBitSize(float.class) >= 256); } } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector.java index 5e7c97dc56d..6f9b5e53ead 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector.java @@ -49,7 +49,8 @@ import static jdk.incubator.vector.VectorOperators.*; * {@code double} values. */ @SuppressWarnings("cast") // warning: redundant cast -public abstract class DoubleVector extends AbstractVector { +public abstract sealed class DoubleVector extends AbstractVector + permits DoubleVector64, DoubleVector128, DoubleVector256, DoubleVector512, DoubleVectorMax { DoubleVector(double[] vec) { super(vec); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector128.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector128.java index 1140d377e9b..8d3ec21ec9b 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector128.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector128.java @@ -31,16 +31,17 @@ import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class DoubleVector128 extends DoubleVector { static final DoubleSpecies VSPECIES = (DoubleSpecies) DoubleVector.SPECIES_128; @@ -358,7 +359,7 @@ final class DoubleVector128 extends DoubleVector { @Override @ForceInline public final DoubleShuffle128 toShuffle() { - return (DoubleShuffle128) toShuffle(vspecies(), false); + return (DoubleShuffle128) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -559,7 +560,7 @@ final class DoubleVector128 extends DoubleVector { } // Mask - + @ValueBased static final class DoubleMask128 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -607,7 +608,7 @@ final class DoubleVector128 extends DoubleVector { @Override DoubleMask128 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -617,7 +618,7 @@ final class DoubleVector128 extends DoubleVector { @Override DoubleMask128 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((DoubleMask128)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -767,16 +768,16 @@ final class DoubleVector128 extends DoubleVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, DoubleMask128.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((DoubleMask128)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((DoubleMask128)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, DoubleMask128.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((DoubleMask128)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((DoubleMask128)m).getBits())); } @ForceInline @@ -784,7 +785,7 @@ final class DoubleVector128 extends DoubleVector { static DoubleMask128 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(DoubleMask128.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final DoubleMask128 TRUE_MASK = new DoubleMask128(true); private static final DoubleMask128 FALSE_MASK = new DoubleMask128(false); @@ -792,7 +793,7 @@ final class DoubleVector128 extends DoubleVector { } // Shuffle - + @ValueBased static final class DoubleShuffle128 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -833,7 +834,7 @@ final class DoubleVector128 extends DoubleVector { @Override @ForceInline public DoubleVector128 toVector() { - return (DoubleVector128) toBitsVector().castShape(vspecies(), 0); + return (DoubleVector128) toBitsVector().castShape(VSPECIES, 0); } @Override @@ -844,7 +845,7 @@ final class DoubleVector128 extends DoubleVector { @Override LongVector128 toBitsVector0() { - return ((LongVector128) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((LongVector128) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -918,7 +919,7 @@ final class DoubleVector128 extends DoubleVector { @ForceInline public final DoubleMask128 laneIsValid() { return (DoubleMask128) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -926,7 +927,7 @@ final class DoubleVector128 extends DoubleVector { public final DoubleShuffle128 rearrange(VectorShuffle shuffle) { DoubleShuffle128 concreteShuffle = (DoubleShuffle128) shuffle; return (DoubleShuffle128) toBitsVector().rearrange(concreteShuffle.cast(LongVector.SPECIES_128)) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -939,7 +940,7 @@ final class DoubleVector128 extends DoubleVector { v = (LongVector128) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (DoubleShuffle128) v.toShuffle(vspecies(), false); + return (DoubleShuffle128) v.toShuffle(VSPECIES, false); } private static long[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector256.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector256.java index 59b7913cfcb..c6bb4b7e3d3 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector256.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector256.java @@ -31,16 +31,17 @@ import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class DoubleVector256 extends DoubleVector { static final DoubleSpecies VSPECIES = (DoubleSpecies) DoubleVector.SPECIES_256; @@ -358,7 +359,7 @@ final class DoubleVector256 extends DoubleVector { @Override @ForceInline public final DoubleShuffle256 toShuffle() { - return (DoubleShuffle256) toShuffle(vspecies(), false); + return (DoubleShuffle256) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -563,7 +564,7 @@ final class DoubleVector256 extends DoubleVector { } // Mask - + @ValueBased static final class DoubleMask256 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -611,7 +612,7 @@ final class DoubleVector256 extends DoubleVector { @Override DoubleMask256 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -621,7 +622,7 @@ final class DoubleVector256 extends DoubleVector { @Override DoubleMask256 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((DoubleMask256)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -771,16 +772,16 @@ final class DoubleVector256 extends DoubleVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, DoubleMask256.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((DoubleMask256)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((DoubleMask256)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, DoubleMask256.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((DoubleMask256)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((DoubleMask256)m).getBits())); } @ForceInline @@ -788,7 +789,7 @@ final class DoubleVector256 extends DoubleVector { static DoubleMask256 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(DoubleMask256.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final DoubleMask256 TRUE_MASK = new DoubleMask256(true); private static final DoubleMask256 FALSE_MASK = new DoubleMask256(false); @@ -796,7 +797,7 @@ final class DoubleVector256 extends DoubleVector { } // Shuffle - + @ValueBased static final class DoubleShuffle256 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -837,7 +838,7 @@ final class DoubleVector256 extends DoubleVector { @Override @ForceInline public DoubleVector256 toVector() { - return (DoubleVector256) toBitsVector().castShape(vspecies(), 0); + return (DoubleVector256) toBitsVector().castShape(VSPECIES, 0); } @Override @@ -848,7 +849,7 @@ final class DoubleVector256 extends DoubleVector { @Override LongVector256 toBitsVector0() { - return ((LongVector256) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((LongVector256) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -922,7 +923,7 @@ final class DoubleVector256 extends DoubleVector { @ForceInline public final DoubleMask256 laneIsValid() { return (DoubleMask256) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -930,7 +931,7 @@ final class DoubleVector256 extends DoubleVector { public final DoubleShuffle256 rearrange(VectorShuffle shuffle) { DoubleShuffle256 concreteShuffle = (DoubleShuffle256) shuffle; return (DoubleShuffle256) toBitsVector().rearrange(concreteShuffle.cast(LongVector.SPECIES_256)) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -943,7 +944,7 @@ final class DoubleVector256 extends DoubleVector { v = (LongVector256) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (DoubleShuffle256) v.toShuffle(vspecies(), false); + return (DoubleShuffle256) v.toShuffle(VSPECIES, false); } private static long[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector512.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector512.java index 8ed21953394..fb1441efc63 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector512.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector512.java @@ -31,16 +31,17 @@ import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class DoubleVector512 extends DoubleVector { static final DoubleSpecies VSPECIES = (DoubleSpecies) DoubleVector.SPECIES_512; @@ -358,7 +359,7 @@ final class DoubleVector512 extends DoubleVector { @Override @ForceInline public final DoubleShuffle512 toShuffle() { - return (DoubleShuffle512) toShuffle(vspecies(), false); + return (DoubleShuffle512) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -571,7 +572,7 @@ final class DoubleVector512 extends DoubleVector { } // Mask - + @ValueBased static final class DoubleMask512 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -619,7 +620,7 @@ final class DoubleVector512 extends DoubleVector { @Override DoubleMask512 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -629,7 +630,7 @@ final class DoubleVector512 extends DoubleVector { @Override DoubleMask512 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((DoubleMask512)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -779,16 +780,16 @@ final class DoubleVector512 extends DoubleVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, DoubleMask512.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((DoubleMask512)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((DoubleMask512)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, DoubleMask512.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((DoubleMask512)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((DoubleMask512)m).getBits())); } @ForceInline @@ -796,7 +797,7 @@ final class DoubleVector512 extends DoubleVector { static DoubleMask512 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(DoubleMask512.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final DoubleMask512 TRUE_MASK = new DoubleMask512(true); private static final DoubleMask512 FALSE_MASK = new DoubleMask512(false); @@ -804,7 +805,7 @@ final class DoubleVector512 extends DoubleVector { } // Shuffle - + @ValueBased static final class DoubleShuffle512 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -845,7 +846,7 @@ final class DoubleVector512 extends DoubleVector { @Override @ForceInline public DoubleVector512 toVector() { - return (DoubleVector512) toBitsVector().castShape(vspecies(), 0); + return (DoubleVector512) toBitsVector().castShape(VSPECIES, 0); } @Override @@ -856,7 +857,7 @@ final class DoubleVector512 extends DoubleVector { @Override LongVector512 toBitsVector0() { - return ((LongVector512) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((LongVector512) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -930,7 +931,7 @@ final class DoubleVector512 extends DoubleVector { @ForceInline public final DoubleMask512 laneIsValid() { return (DoubleMask512) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -938,7 +939,7 @@ final class DoubleVector512 extends DoubleVector { public final DoubleShuffle512 rearrange(VectorShuffle shuffle) { DoubleShuffle512 concreteShuffle = (DoubleShuffle512) shuffle; return (DoubleShuffle512) toBitsVector().rearrange(concreteShuffle.cast(LongVector.SPECIES_512)) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -951,7 +952,7 @@ final class DoubleVector512 extends DoubleVector { v = (LongVector512) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (DoubleShuffle512) v.toShuffle(vspecies(), false); + return (DoubleShuffle512) v.toShuffle(VSPECIES, false); } private static long[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector64.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector64.java index 7e1a8cf768d..5583cff80e1 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector64.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector64.java @@ -31,16 +31,17 @@ import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class DoubleVector64 extends DoubleVector { static final DoubleSpecies VSPECIES = (DoubleSpecies) DoubleVector.SPECIES_64; @@ -358,7 +359,7 @@ final class DoubleVector64 extends DoubleVector { @Override @ForceInline public final DoubleShuffle64 toShuffle() { - return (DoubleShuffle64) toShuffle(vspecies(), false); + return (DoubleShuffle64) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -557,7 +558,7 @@ final class DoubleVector64 extends DoubleVector { } // Mask - + @ValueBased static final class DoubleMask64 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -605,7 +606,7 @@ final class DoubleVector64 extends DoubleVector { @Override DoubleMask64 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -615,7 +616,7 @@ final class DoubleVector64 extends DoubleVector { @Override DoubleMask64 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((DoubleMask64)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -765,16 +766,16 @@ final class DoubleVector64 extends DoubleVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, DoubleMask64.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((DoubleMask64)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((DoubleMask64)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, DoubleMask64.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((DoubleMask64)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((DoubleMask64)m).getBits())); } @ForceInline @@ -782,7 +783,7 @@ final class DoubleVector64 extends DoubleVector { static DoubleMask64 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(DoubleMask64.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final DoubleMask64 TRUE_MASK = new DoubleMask64(true); private static final DoubleMask64 FALSE_MASK = new DoubleMask64(false); @@ -790,7 +791,7 @@ final class DoubleVector64 extends DoubleVector { } // Shuffle - + @ValueBased static final class DoubleShuffle64 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -831,7 +832,7 @@ final class DoubleVector64 extends DoubleVector { @Override @ForceInline public DoubleVector64 toVector() { - return (DoubleVector64) toBitsVector().castShape(vspecies(), 0); + return (DoubleVector64) toBitsVector().castShape(VSPECIES, 0); } @Override @@ -842,7 +843,7 @@ final class DoubleVector64 extends DoubleVector { @Override LongVector64 toBitsVector0() { - return ((LongVector64) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((LongVector64) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -916,7 +917,7 @@ final class DoubleVector64 extends DoubleVector { @ForceInline public final DoubleMask64 laneIsValid() { return (DoubleMask64) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -924,7 +925,7 @@ final class DoubleVector64 extends DoubleVector { public final DoubleShuffle64 rearrange(VectorShuffle shuffle) { DoubleShuffle64 concreteShuffle = (DoubleShuffle64) shuffle; return (DoubleShuffle64) toBitsVector().rearrange(concreteShuffle.cast(LongVector.SPECIES_64)) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -937,7 +938,7 @@ final class DoubleVector64 extends DoubleVector { v = (LongVector64) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (DoubleShuffle64) v.toShuffle(vspecies(), false); + return (DoubleShuffle64) v.toShuffle(VSPECIES, false); } private static long[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVectorMax.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVectorMax.java index 46c090e066e..41272a5a5e5 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVectorMax.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVectorMax.java @@ -31,16 +31,17 @@ import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class DoubleVectorMax extends DoubleVector { static final DoubleSpecies VSPECIES = (DoubleSpecies) DoubleVector.SPECIES_MAX; @@ -358,7 +359,7 @@ final class DoubleVectorMax extends DoubleVector { @Override @ForceInline public final DoubleShuffleMax toShuffle() { - return (DoubleShuffleMax) toShuffle(vspecies(), false); + return (DoubleShuffleMax) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -556,7 +557,7 @@ final class DoubleVectorMax extends DoubleVector { } // Mask - + @ValueBased static final class DoubleMaskMax extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -604,7 +605,7 @@ final class DoubleVectorMax extends DoubleVector { @Override DoubleMaskMax uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -614,7 +615,7 @@ final class DoubleVectorMax extends DoubleVector { @Override DoubleMaskMax bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((DoubleMaskMax)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -764,16 +765,16 @@ final class DoubleVectorMax extends DoubleVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, DoubleMaskMax.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((DoubleMaskMax)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((DoubleMaskMax)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, DoubleMaskMax.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((DoubleMaskMax)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((DoubleMaskMax)m).getBits())); } @ForceInline @@ -781,7 +782,7 @@ final class DoubleVectorMax extends DoubleVector { static DoubleMaskMax maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(DoubleMaskMax.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final DoubleMaskMax TRUE_MASK = new DoubleMaskMax(true); private static final DoubleMaskMax FALSE_MASK = new DoubleMaskMax(false); @@ -789,7 +790,7 @@ final class DoubleVectorMax extends DoubleVector { } // Shuffle - + @ValueBased static final class DoubleShuffleMax extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -830,7 +831,7 @@ final class DoubleVectorMax extends DoubleVector { @Override @ForceInline public DoubleVectorMax toVector() { - return (DoubleVectorMax) toBitsVector().castShape(vspecies(), 0); + return (DoubleVectorMax) toBitsVector().castShape(VSPECIES, 0); } @Override @@ -841,7 +842,7 @@ final class DoubleVectorMax extends DoubleVector { @Override LongVectorMax toBitsVector0() { - return ((LongVectorMax) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((LongVectorMax) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -915,7 +916,7 @@ final class DoubleVectorMax extends DoubleVector { @ForceInline public final DoubleMaskMax laneIsValid() { return (DoubleMaskMax) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -923,7 +924,7 @@ final class DoubleVectorMax extends DoubleVector { public final DoubleShuffleMax rearrange(VectorShuffle shuffle) { DoubleShuffleMax concreteShuffle = (DoubleShuffleMax) shuffle; return (DoubleShuffleMax) toBitsVector().rearrange(concreteShuffle.cast(LongVector.SPECIES_MAX)) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -936,7 +937,7 @@ final class DoubleVectorMax extends DoubleVector { v = (LongVectorMax) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (DoubleShuffleMax) v.toShuffle(vspecies(), false); + return (DoubleShuffleMax) v.toShuffle(VSPECIES, false); } private static long[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float16Consts.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float16Consts.java index 48c4d2199b1..b70b11b0a49 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float16Consts.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float16Consts.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, 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,7 +34,7 @@ import static jdk.incubator.vector.Float16.SIZE; * {@code Float16} type. */ -class Float16Consts { +final class Float16Consts { /** * Don't let anyone instantiate this class. */ diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector.java index 5862a295fa3..cdf2532e4d9 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector.java @@ -49,7 +49,8 @@ import static jdk.incubator.vector.VectorOperators.*; * {@code float} values. */ @SuppressWarnings("cast") // warning: redundant cast -public abstract class FloatVector extends AbstractVector { +public abstract sealed class FloatVector extends AbstractVector + permits FloatVector64, FloatVector128, FloatVector256, FloatVector512, FloatVectorMax { FloatVector(float[] vec) { super(vec); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector128.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector128.java index 1e3867e84fc..24888e966da 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector128.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector128.java @@ -25,22 +25,22 @@ package jdk.incubator.vector; import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class FloatVector128 extends FloatVector { static final FloatSpecies VSPECIES = (FloatSpecies) FloatVector.SPECIES_128; @@ -358,7 +358,7 @@ final class FloatVector128 extends FloatVector { @Override @ForceInline public final FloatShuffle128 toShuffle() { - return (FloatShuffle128) toShuffle(vspecies(), false); + return (FloatShuffle128) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -563,7 +563,7 @@ final class FloatVector128 extends FloatVector { } // Mask - + @ValueBased static final class FloatMask128 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -611,7 +611,7 @@ final class FloatVector128 extends FloatVector { @Override FloatMask128 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -621,7 +621,7 @@ final class FloatVector128 extends FloatVector { @Override FloatMask128 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((FloatMask128)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -771,16 +771,16 @@ final class FloatVector128 extends FloatVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, FloatMask128.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((FloatMask128)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((FloatMask128)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, FloatMask128.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((FloatMask128)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((FloatMask128)m).getBits())); } @ForceInline @@ -788,7 +788,7 @@ final class FloatVector128 extends FloatVector { static FloatMask128 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(FloatMask128.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final FloatMask128 TRUE_MASK = new FloatMask128(true); private static final FloatMask128 FALSE_MASK = new FloatMask128(false); @@ -796,7 +796,7 @@ final class FloatVector128 extends FloatVector { } // Shuffle - + @ValueBased static final class FloatShuffle128 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -837,7 +837,7 @@ final class FloatVector128 extends FloatVector { @Override @ForceInline public FloatVector128 toVector() { - return (FloatVector128) toBitsVector().castShape(vspecies(), 0); + return (FloatVector128) toBitsVector().castShape(VSPECIES, 0); } @Override @@ -848,7 +848,7 @@ final class FloatVector128 extends FloatVector { @Override IntVector128 toBitsVector0() { - return ((IntVector128) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((IntVector128) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -873,7 +873,7 @@ final class FloatVector128 extends FloatVector { @ForceInline public final FloatMask128 laneIsValid() { return (FloatMask128) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -881,7 +881,7 @@ final class FloatVector128 extends FloatVector { public final FloatShuffle128 rearrange(VectorShuffle shuffle) { FloatShuffle128 concreteShuffle = (FloatShuffle128) shuffle; return (FloatShuffle128) toBitsVector().rearrange(concreteShuffle.cast(IntVector.SPECIES_128)) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -894,7 +894,7 @@ final class FloatVector128 extends FloatVector { v = (IntVector128) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (FloatShuffle128) v.toShuffle(vspecies(), false); + return (FloatShuffle128) v.toShuffle(VSPECIES, false); } private static int[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector256.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector256.java index f267025972d..ecbd80046f4 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector256.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector256.java @@ -25,22 +25,22 @@ package jdk.incubator.vector; import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class FloatVector256 extends FloatVector { static final FloatSpecies VSPECIES = (FloatSpecies) FloatVector.SPECIES_256; @@ -358,7 +358,7 @@ final class FloatVector256 extends FloatVector { @Override @ForceInline public final FloatShuffle256 toShuffle() { - return (FloatShuffle256) toShuffle(vspecies(), false); + return (FloatShuffle256) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -571,7 +571,7 @@ final class FloatVector256 extends FloatVector { } // Mask - + @ValueBased static final class FloatMask256 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -619,7 +619,7 @@ final class FloatVector256 extends FloatVector { @Override FloatMask256 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -629,7 +629,7 @@ final class FloatVector256 extends FloatVector { @Override FloatMask256 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((FloatMask256)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -779,16 +779,16 @@ final class FloatVector256 extends FloatVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, FloatMask256.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((FloatMask256)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((FloatMask256)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, FloatMask256.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((FloatMask256)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((FloatMask256)m).getBits())); } @ForceInline @@ -796,7 +796,7 @@ final class FloatVector256 extends FloatVector { static FloatMask256 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(FloatMask256.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final FloatMask256 TRUE_MASK = new FloatMask256(true); private static final FloatMask256 FALSE_MASK = new FloatMask256(false); @@ -804,7 +804,7 @@ final class FloatVector256 extends FloatVector { } // Shuffle - + @ValueBased static final class FloatShuffle256 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -845,7 +845,7 @@ final class FloatVector256 extends FloatVector { @Override @ForceInline public FloatVector256 toVector() { - return (FloatVector256) toBitsVector().castShape(vspecies(), 0); + return (FloatVector256) toBitsVector().castShape(VSPECIES, 0); } @Override @@ -856,7 +856,7 @@ final class FloatVector256 extends FloatVector { @Override IntVector256 toBitsVector0() { - return ((IntVector256) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((IntVector256) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -881,7 +881,7 @@ final class FloatVector256 extends FloatVector { @ForceInline public final FloatMask256 laneIsValid() { return (FloatMask256) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -889,7 +889,7 @@ final class FloatVector256 extends FloatVector { public final FloatShuffle256 rearrange(VectorShuffle shuffle) { FloatShuffle256 concreteShuffle = (FloatShuffle256) shuffle; return (FloatShuffle256) toBitsVector().rearrange(concreteShuffle.cast(IntVector.SPECIES_256)) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -902,7 +902,7 @@ final class FloatVector256 extends FloatVector { v = (IntVector256) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (FloatShuffle256) v.toShuffle(vspecies(), false); + return (FloatShuffle256) v.toShuffle(VSPECIES, false); } private static int[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector512.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector512.java index 439e26f0d89..b5a934dd90c 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector512.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector512.java @@ -25,22 +25,22 @@ package jdk.incubator.vector; import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class FloatVector512 extends FloatVector { static final FloatSpecies VSPECIES = (FloatSpecies) FloatVector.SPECIES_512; @@ -358,7 +358,7 @@ final class FloatVector512 extends FloatVector { @Override @ForceInline public final FloatShuffle512 toShuffle() { - return (FloatShuffle512) toShuffle(vspecies(), false); + return (FloatShuffle512) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -587,7 +587,7 @@ final class FloatVector512 extends FloatVector { } // Mask - + @ValueBased static final class FloatMask512 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -635,7 +635,7 @@ final class FloatVector512 extends FloatVector { @Override FloatMask512 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -645,7 +645,7 @@ final class FloatVector512 extends FloatVector { @Override FloatMask512 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((FloatMask512)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -795,16 +795,16 @@ final class FloatVector512 extends FloatVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, FloatMask512.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((FloatMask512)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((FloatMask512)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, FloatMask512.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((FloatMask512)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((FloatMask512)m).getBits())); } @ForceInline @@ -812,7 +812,7 @@ final class FloatVector512 extends FloatVector { static FloatMask512 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(FloatMask512.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final FloatMask512 TRUE_MASK = new FloatMask512(true); private static final FloatMask512 FALSE_MASK = new FloatMask512(false); @@ -820,7 +820,7 @@ final class FloatVector512 extends FloatVector { } // Shuffle - + @ValueBased static final class FloatShuffle512 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -861,7 +861,7 @@ final class FloatVector512 extends FloatVector { @Override @ForceInline public FloatVector512 toVector() { - return (FloatVector512) toBitsVector().castShape(vspecies(), 0); + return (FloatVector512) toBitsVector().castShape(VSPECIES, 0); } @Override @@ -872,7 +872,7 @@ final class FloatVector512 extends FloatVector { @Override IntVector512 toBitsVector0() { - return ((IntVector512) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((IntVector512) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -897,7 +897,7 @@ final class FloatVector512 extends FloatVector { @ForceInline public final FloatMask512 laneIsValid() { return (FloatMask512) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -905,7 +905,7 @@ final class FloatVector512 extends FloatVector { public final FloatShuffle512 rearrange(VectorShuffle shuffle) { FloatShuffle512 concreteShuffle = (FloatShuffle512) shuffle; return (FloatShuffle512) toBitsVector().rearrange(concreteShuffle.cast(IntVector.SPECIES_512)) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -918,7 +918,7 @@ final class FloatVector512 extends FloatVector { v = (IntVector512) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (FloatShuffle512) v.toShuffle(vspecies(), false); + return (FloatShuffle512) v.toShuffle(VSPECIES, false); } private static int[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector64.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector64.java index 9e81a52d27b..4d3118739ea 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector64.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector64.java @@ -25,22 +25,22 @@ package jdk.incubator.vector; import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class FloatVector64 extends FloatVector { static final FloatSpecies VSPECIES = (FloatSpecies) FloatVector.SPECIES_64; @@ -358,7 +358,7 @@ final class FloatVector64 extends FloatVector { @Override @ForceInline public final FloatShuffle64 toShuffle() { - return (FloatShuffle64) toShuffle(vspecies(), false); + return (FloatShuffle64) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -559,7 +559,7 @@ final class FloatVector64 extends FloatVector { } // Mask - + @ValueBased static final class FloatMask64 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -607,7 +607,7 @@ final class FloatVector64 extends FloatVector { @Override FloatMask64 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -617,7 +617,7 @@ final class FloatVector64 extends FloatVector { @Override FloatMask64 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((FloatMask64)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -767,16 +767,16 @@ final class FloatVector64 extends FloatVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, FloatMask64.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((FloatMask64)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((FloatMask64)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, FloatMask64.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((FloatMask64)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((FloatMask64)m).getBits())); } @ForceInline @@ -784,7 +784,7 @@ final class FloatVector64 extends FloatVector { static FloatMask64 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(FloatMask64.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final FloatMask64 TRUE_MASK = new FloatMask64(true); private static final FloatMask64 FALSE_MASK = new FloatMask64(false); @@ -792,7 +792,7 @@ final class FloatVector64 extends FloatVector { } // Shuffle - + @ValueBased static final class FloatShuffle64 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -833,7 +833,7 @@ final class FloatVector64 extends FloatVector { @Override @ForceInline public FloatVector64 toVector() { - return (FloatVector64) toBitsVector().castShape(vspecies(), 0); + return (FloatVector64) toBitsVector().castShape(VSPECIES, 0); } @Override @@ -844,7 +844,7 @@ final class FloatVector64 extends FloatVector { @Override IntVector64 toBitsVector0() { - return ((IntVector64) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((IntVector64) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -869,7 +869,7 @@ final class FloatVector64 extends FloatVector { @ForceInline public final FloatMask64 laneIsValid() { return (FloatMask64) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -877,7 +877,7 @@ final class FloatVector64 extends FloatVector { public final FloatShuffle64 rearrange(VectorShuffle shuffle) { FloatShuffle64 concreteShuffle = (FloatShuffle64) shuffle; return (FloatShuffle64) toBitsVector().rearrange(concreteShuffle.cast(IntVector.SPECIES_64)) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -890,7 +890,7 @@ final class FloatVector64 extends FloatVector { v = (IntVector64) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (FloatShuffle64) v.toShuffle(vspecies(), false); + return (FloatShuffle64) v.toShuffle(VSPECIES, false); } private static int[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVectorMax.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVectorMax.java index 4813f153544..f115a1c79b8 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVectorMax.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVectorMax.java @@ -25,22 +25,22 @@ package jdk.incubator.vector; import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class FloatVectorMax extends FloatVector { static final FloatSpecies VSPECIES = (FloatSpecies) FloatVector.SPECIES_MAX; @@ -358,7 +358,7 @@ final class FloatVectorMax extends FloatVector { @Override @ForceInline public final FloatShuffleMax toShuffle() { - return (FloatShuffleMax) toShuffle(vspecies(), false); + return (FloatShuffleMax) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -556,7 +556,7 @@ final class FloatVectorMax extends FloatVector { } // Mask - + @ValueBased static final class FloatMaskMax extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -604,7 +604,7 @@ final class FloatVectorMax extends FloatVector { @Override FloatMaskMax uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -614,7 +614,7 @@ final class FloatVectorMax extends FloatVector { @Override FloatMaskMax bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((FloatMaskMax)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -764,16 +764,16 @@ final class FloatVectorMax extends FloatVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, FloatMaskMax.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((FloatMaskMax)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((FloatMaskMax)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, FloatMaskMax.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((FloatMaskMax)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((FloatMaskMax)m).getBits())); } @ForceInline @@ -781,7 +781,7 @@ final class FloatVectorMax extends FloatVector { static FloatMaskMax maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(FloatMaskMax.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final FloatMaskMax TRUE_MASK = new FloatMaskMax(true); private static final FloatMaskMax FALSE_MASK = new FloatMaskMax(false); @@ -789,7 +789,7 @@ final class FloatVectorMax extends FloatVector { } // Shuffle - + @ValueBased static final class FloatShuffleMax extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -830,7 +830,7 @@ final class FloatVectorMax extends FloatVector { @Override @ForceInline public FloatVectorMax toVector() { - return (FloatVectorMax) toBitsVector().castShape(vspecies(), 0); + return (FloatVectorMax) toBitsVector().castShape(VSPECIES, 0); } @Override @@ -841,7 +841,7 @@ final class FloatVectorMax extends FloatVector { @Override IntVectorMax toBitsVector0() { - return ((IntVectorMax) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((IntVectorMax) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -866,7 +866,7 @@ final class FloatVectorMax extends FloatVector { @ForceInline public final FloatMaskMax laneIsValid() { return (FloatMaskMax) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -874,7 +874,7 @@ final class FloatVectorMax extends FloatVector { public final FloatShuffleMax rearrange(VectorShuffle shuffle) { FloatShuffleMax concreteShuffle = (FloatShuffleMax) shuffle; return (FloatShuffleMax) toBitsVector().rearrange(concreteShuffle.cast(IntVector.SPECIES_MAX)) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -887,7 +887,7 @@ final class FloatVectorMax extends FloatVector { v = (IntVectorMax) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (FloatShuffleMax) v.toShuffle(vspecies(), false); + return (FloatShuffleMax) v.toShuffle(VSPECIES, false); } private static int[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector.java index 445c4dfb006..37b7e3eeae4 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector.java @@ -49,7 +49,8 @@ import static jdk.incubator.vector.VectorOperators.*; * {@code int} values. */ @SuppressWarnings("cast") // warning: redundant cast -public abstract class IntVector extends AbstractVector { +public abstract sealed class IntVector extends AbstractVector + permits IntVector64, IntVector128, IntVector256, IntVector512, IntVectorMax { IntVector(int[] vec) { super(vec); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector128.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector128.java index cc8f31a4bc2..f64328e2a1e 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector128.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector128.java @@ -25,22 +25,22 @@ package jdk.incubator.vector; import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class IntVector128 extends IntVector { static final IntSpecies VSPECIES = (IntSpecies) IntVector.SPECIES_128; @@ -371,7 +371,7 @@ final class IntVector128 extends IntVector { @Override @ForceInline public final IntShuffle128 toShuffle() { - return (IntShuffle128) toShuffle(vspecies(), false); + return (IntShuffle128) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -574,7 +574,7 @@ final class IntVector128 extends IntVector { } // Mask - + @ValueBased static final class IntMask128 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -622,7 +622,7 @@ final class IntVector128 extends IntVector { @Override IntMask128 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -632,7 +632,7 @@ final class IntVector128 extends IntVector { @Override IntMask128 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((IntMask128)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -782,16 +782,16 @@ final class IntVector128 extends IntVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, IntMask128.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((IntMask128)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((IntMask128)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, IntMask128.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((IntMask128)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((IntMask128)m).getBits())); } @ForceInline @@ -799,7 +799,7 @@ final class IntVector128 extends IntVector { static IntMask128 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(IntMask128.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final IntMask128 TRUE_MASK = new IntMask128(true); private static final IntMask128 FALSE_MASK = new IntMask128(false); @@ -807,7 +807,7 @@ final class IntVector128 extends IntVector { } // Shuffle - + @ValueBased static final class IntShuffle128 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -859,7 +859,7 @@ final class IntVector128 extends IntVector { @Override IntVector128 toBitsVector0() { - return ((IntVector128) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((IntVector128) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -884,7 +884,7 @@ final class IntVector128 extends IntVector { @ForceInline public final IntMask128 laneIsValid() { return (IntMask128) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -892,7 +892,7 @@ final class IntVector128 extends IntVector { public final IntShuffle128 rearrange(VectorShuffle shuffle) { IntShuffle128 concreteShuffle = (IntShuffle128) shuffle; return (IntShuffle128) toBitsVector().rearrange(concreteShuffle) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -905,7 +905,7 @@ final class IntVector128 extends IntVector { v = (IntVector128) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (IntShuffle128) v.toShuffle(vspecies(), false); + return (IntShuffle128) v.toShuffle(VSPECIES, false); } private static int[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector256.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector256.java index 0630cd958f2..58a1667d2ac 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector256.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector256.java @@ -25,22 +25,22 @@ package jdk.incubator.vector; import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class IntVector256 extends IntVector { static final IntSpecies VSPECIES = (IntSpecies) IntVector.SPECIES_256; @@ -371,7 +371,7 @@ final class IntVector256 extends IntVector { @Override @ForceInline public final IntShuffle256 toShuffle() { - return (IntShuffle256) toShuffle(vspecies(), false); + return (IntShuffle256) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -582,7 +582,7 @@ final class IntVector256 extends IntVector { } // Mask - + @ValueBased static final class IntMask256 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -630,7 +630,7 @@ final class IntVector256 extends IntVector { @Override IntMask256 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -640,7 +640,7 @@ final class IntVector256 extends IntVector { @Override IntMask256 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((IntMask256)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -790,16 +790,16 @@ final class IntVector256 extends IntVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, IntMask256.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((IntMask256)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((IntMask256)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, IntMask256.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((IntMask256)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((IntMask256)m).getBits())); } @ForceInline @@ -807,7 +807,7 @@ final class IntVector256 extends IntVector { static IntMask256 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(IntMask256.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final IntMask256 TRUE_MASK = new IntMask256(true); private static final IntMask256 FALSE_MASK = new IntMask256(false); @@ -815,7 +815,7 @@ final class IntVector256 extends IntVector { } // Shuffle - + @ValueBased static final class IntShuffle256 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -867,7 +867,7 @@ final class IntVector256 extends IntVector { @Override IntVector256 toBitsVector0() { - return ((IntVector256) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((IntVector256) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -892,7 +892,7 @@ final class IntVector256 extends IntVector { @ForceInline public final IntMask256 laneIsValid() { return (IntMask256) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -900,7 +900,7 @@ final class IntVector256 extends IntVector { public final IntShuffle256 rearrange(VectorShuffle shuffle) { IntShuffle256 concreteShuffle = (IntShuffle256) shuffle; return (IntShuffle256) toBitsVector().rearrange(concreteShuffle) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -913,7 +913,7 @@ final class IntVector256 extends IntVector { v = (IntVector256) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (IntShuffle256) v.toShuffle(vspecies(), false); + return (IntShuffle256) v.toShuffle(VSPECIES, false); } private static int[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector512.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector512.java index 92eb5a0f2d2..ac48e589a05 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector512.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector512.java @@ -25,22 +25,22 @@ package jdk.incubator.vector; import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class IntVector512 extends IntVector { static final IntSpecies VSPECIES = (IntSpecies) IntVector.SPECIES_512; @@ -371,7 +371,7 @@ final class IntVector512 extends IntVector { @Override @ForceInline public final IntShuffle512 toShuffle() { - return (IntShuffle512) toShuffle(vspecies(), false); + return (IntShuffle512) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -598,7 +598,7 @@ final class IntVector512 extends IntVector { } // Mask - + @ValueBased static final class IntMask512 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -646,7 +646,7 @@ final class IntVector512 extends IntVector { @Override IntMask512 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -656,7 +656,7 @@ final class IntVector512 extends IntVector { @Override IntMask512 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((IntMask512)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -806,16 +806,16 @@ final class IntVector512 extends IntVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, IntMask512.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((IntMask512)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((IntMask512)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, IntMask512.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((IntMask512)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((IntMask512)m).getBits())); } @ForceInline @@ -823,7 +823,7 @@ final class IntVector512 extends IntVector { static IntMask512 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(IntMask512.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final IntMask512 TRUE_MASK = new IntMask512(true); private static final IntMask512 FALSE_MASK = new IntMask512(false); @@ -831,7 +831,7 @@ final class IntVector512 extends IntVector { } // Shuffle - + @ValueBased static final class IntShuffle512 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -883,7 +883,7 @@ final class IntVector512 extends IntVector { @Override IntVector512 toBitsVector0() { - return ((IntVector512) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((IntVector512) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -908,7 +908,7 @@ final class IntVector512 extends IntVector { @ForceInline public final IntMask512 laneIsValid() { return (IntMask512) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -916,7 +916,7 @@ final class IntVector512 extends IntVector { public final IntShuffle512 rearrange(VectorShuffle shuffle) { IntShuffle512 concreteShuffle = (IntShuffle512) shuffle; return (IntShuffle512) toBitsVector().rearrange(concreteShuffle) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -929,7 +929,7 @@ final class IntVector512 extends IntVector { v = (IntVector512) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (IntShuffle512) v.toShuffle(vspecies(), false); + return (IntShuffle512) v.toShuffle(VSPECIES, false); } private static int[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector64.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector64.java index c3f92285034..25329aa81aa 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector64.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector64.java @@ -25,22 +25,22 @@ package jdk.incubator.vector; import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class IntVector64 extends IntVector { static final IntSpecies VSPECIES = (IntSpecies) IntVector.SPECIES_64; @@ -371,7 +371,7 @@ final class IntVector64 extends IntVector { @Override @ForceInline public final IntShuffle64 toShuffle() { - return (IntShuffle64) toShuffle(vspecies(), false); + return (IntShuffle64) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -570,7 +570,7 @@ final class IntVector64 extends IntVector { } // Mask - + @ValueBased static final class IntMask64 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -618,7 +618,7 @@ final class IntVector64 extends IntVector { @Override IntMask64 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -628,7 +628,7 @@ final class IntVector64 extends IntVector { @Override IntMask64 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((IntMask64)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -778,16 +778,16 @@ final class IntVector64 extends IntVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, IntMask64.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((IntMask64)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((IntMask64)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, IntMask64.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((IntMask64)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((IntMask64)m).getBits())); } @ForceInline @@ -795,7 +795,7 @@ final class IntVector64 extends IntVector { static IntMask64 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(IntMask64.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final IntMask64 TRUE_MASK = new IntMask64(true); private static final IntMask64 FALSE_MASK = new IntMask64(false); @@ -803,7 +803,7 @@ final class IntVector64 extends IntVector { } // Shuffle - + @ValueBased static final class IntShuffle64 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -855,7 +855,7 @@ final class IntVector64 extends IntVector { @Override IntVector64 toBitsVector0() { - return ((IntVector64) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((IntVector64) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -880,7 +880,7 @@ final class IntVector64 extends IntVector { @ForceInline public final IntMask64 laneIsValid() { return (IntMask64) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -888,7 +888,7 @@ final class IntVector64 extends IntVector { public final IntShuffle64 rearrange(VectorShuffle shuffle) { IntShuffle64 concreteShuffle = (IntShuffle64) shuffle; return (IntShuffle64) toBitsVector().rearrange(concreteShuffle) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -901,7 +901,7 @@ final class IntVector64 extends IntVector { v = (IntVector64) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (IntShuffle64) v.toShuffle(vspecies(), false); + return (IntShuffle64) v.toShuffle(VSPECIES, false); } private static int[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVectorMax.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVectorMax.java index 8d3c251536c..348fda59381 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVectorMax.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVectorMax.java @@ -25,22 +25,22 @@ package jdk.incubator.vector; import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class IntVectorMax extends IntVector { static final IntSpecies VSPECIES = (IntSpecies) IntVector.SPECIES_MAX; @@ -371,7 +371,7 @@ final class IntVectorMax extends IntVector { @Override @ForceInline public final IntShuffleMax toShuffle() { - return (IntShuffleMax) toShuffle(vspecies(), false); + return (IntShuffleMax) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -568,7 +568,7 @@ final class IntVectorMax extends IntVector { } // Mask - + @ValueBased static final class IntMaskMax extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -616,7 +616,7 @@ final class IntVectorMax extends IntVector { @Override IntMaskMax uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -626,7 +626,7 @@ final class IntVectorMax extends IntVector { @Override IntMaskMax bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((IntMaskMax)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -776,16 +776,16 @@ final class IntVectorMax extends IntVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, IntMaskMax.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((IntMaskMax)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((IntMaskMax)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, IntMaskMax.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((IntMaskMax)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((IntMaskMax)m).getBits())); } @ForceInline @@ -793,7 +793,7 @@ final class IntVectorMax extends IntVector { static IntMaskMax maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(IntMaskMax.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final IntMaskMax TRUE_MASK = new IntMaskMax(true); private static final IntMaskMax FALSE_MASK = new IntMaskMax(false); @@ -812,7 +812,7 @@ final class IntVectorMax extends IntVector { } // Shuffle - + @ValueBased static final class IntShuffleMax extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -864,7 +864,7 @@ final class IntVectorMax extends IntVector { @Override IntVectorMax toBitsVector0() { - return ((IntVectorMax) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((IntVectorMax) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -889,7 +889,7 @@ final class IntVectorMax extends IntVector { @ForceInline public final IntMaskMax laneIsValid() { return (IntMaskMax) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -897,7 +897,7 @@ final class IntVectorMax extends IntVector { public final IntShuffleMax rearrange(VectorShuffle shuffle) { IntShuffleMax concreteShuffle = (IntShuffleMax) shuffle; return (IntShuffleMax) toBitsVector().rearrange(concreteShuffle) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -910,7 +910,7 @@ final class IntVectorMax extends IntVector { v = (IntVectorMax) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (IntShuffleMax) v.toShuffle(vspecies(), false); + return (IntShuffleMax) v.toShuffle(VSPECIES, false); } private static int[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector.java index 7ba0af6c139..36300cf892b 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector.java @@ -49,7 +49,8 @@ import static jdk.incubator.vector.VectorOperators.*; * {@code long} values. */ @SuppressWarnings("cast") // warning: redundant cast -public abstract class LongVector extends AbstractVector { +public abstract sealed class LongVector extends AbstractVector + permits LongVector64, LongVector128, LongVector256, LongVector512, LongVectorMax { LongVector(long[] vec) { super(vec); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector128.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector128.java index f8dad12ff89..7ce60b2efe0 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector128.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector128.java @@ -31,16 +31,17 @@ import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class LongVector128 extends LongVector { static final LongSpecies VSPECIES = (LongSpecies) LongVector.SPECIES_128; @@ -366,7 +367,7 @@ final class LongVector128 extends LongVector { @Override @ForceInline public final LongShuffle128 toShuffle() { - return (LongShuffle128) toShuffle(vspecies(), false); + return (LongShuffle128) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -560,7 +561,7 @@ final class LongVector128 extends LongVector { } // Mask - + @ValueBased static final class LongMask128 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -608,7 +609,7 @@ final class LongVector128 extends LongVector { @Override LongMask128 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -618,7 +619,7 @@ final class LongVector128 extends LongVector { @Override LongMask128 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((LongMask128)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -768,16 +769,16 @@ final class LongVector128 extends LongVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, LongMask128.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((LongMask128)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((LongMask128)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, LongMask128.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((LongMask128)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((LongMask128)m).getBits())); } @ForceInline @@ -785,7 +786,7 @@ final class LongVector128 extends LongVector { static LongMask128 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(LongMask128.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final LongMask128 TRUE_MASK = new LongMask128(true); private static final LongMask128 FALSE_MASK = new LongMask128(false); @@ -793,7 +794,7 @@ final class LongVector128 extends LongVector { } // Shuffle - + @ValueBased static final class LongShuffle128 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -845,7 +846,7 @@ final class LongVector128 extends LongVector { @Override LongVector128 toBitsVector0() { - return ((LongVector128) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((LongVector128) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -919,7 +920,7 @@ final class LongVector128 extends LongVector { @ForceInline public final LongMask128 laneIsValid() { return (LongMask128) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -927,7 +928,7 @@ final class LongVector128 extends LongVector { public final LongShuffle128 rearrange(VectorShuffle shuffle) { LongShuffle128 concreteShuffle = (LongShuffle128) shuffle; return (LongShuffle128) toBitsVector().rearrange(concreteShuffle) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -940,7 +941,7 @@ final class LongVector128 extends LongVector { v = (LongVector128) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (LongShuffle128) v.toShuffle(vspecies(), false); + return (LongShuffle128) v.toShuffle(VSPECIES, false); } private static long[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector256.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector256.java index 144e2c1c64d..110a54c547f 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector256.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector256.java @@ -31,16 +31,17 @@ import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class LongVector256 extends LongVector { static final LongSpecies VSPECIES = (LongSpecies) LongVector.SPECIES_256; @@ -366,7 +367,7 @@ final class LongVector256 extends LongVector { @Override @ForceInline public final LongShuffle256 toShuffle() { - return (LongShuffle256) toShuffle(vspecies(), false); + return (LongShuffle256) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -564,7 +565,7 @@ final class LongVector256 extends LongVector { } // Mask - + @ValueBased static final class LongMask256 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -612,7 +613,7 @@ final class LongVector256 extends LongVector { @Override LongMask256 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -622,7 +623,7 @@ final class LongVector256 extends LongVector { @Override LongMask256 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((LongMask256)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -772,16 +773,16 @@ final class LongVector256 extends LongVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, LongMask256.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((LongMask256)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((LongMask256)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, LongMask256.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((LongMask256)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((LongMask256)m).getBits())); } @ForceInline @@ -789,7 +790,7 @@ final class LongVector256 extends LongVector { static LongMask256 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(LongMask256.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final LongMask256 TRUE_MASK = new LongMask256(true); private static final LongMask256 FALSE_MASK = new LongMask256(false); @@ -797,7 +798,7 @@ final class LongVector256 extends LongVector { } // Shuffle - + @ValueBased static final class LongShuffle256 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -849,7 +850,7 @@ final class LongVector256 extends LongVector { @Override LongVector256 toBitsVector0() { - return ((LongVector256) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((LongVector256) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -923,7 +924,7 @@ final class LongVector256 extends LongVector { @ForceInline public final LongMask256 laneIsValid() { return (LongMask256) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -931,7 +932,7 @@ final class LongVector256 extends LongVector { public final LongShuffle256 rearrange(VectorShuffle shuffle) { LongShuffle256 concreteShuffle = (LongShuffle256) shuffle; return (LongShuffle256) toBitsVector().rearrange(concreteShuffle) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -944,7 +945,7 @@ final class LongVector256 extends LongVector { v = (LongVector256) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (LongShuffle256) v.toShuffle(vspecies(), false); + return (LongShuffle256) v.toShuffle(VSPECIES, false); } private static long[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector512.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector512.java index b49d0c7c147..3502f209c3b 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector512.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector512.java @@ -31,16 +31,17 @@ import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class LongVector512 extends LongVector { static final LongSpecies VSPECIES = (LongSpecies) LongVector.SPECIES_512; @@ -366,7 +367,7 @@ final class LongVector512 extends LongVector { @Override @ForceInline public final LongShuffle512 toShuffle() { - return (LongShuffle512) toShuffle(vspecies(), false); + return (LongShuffle512) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -572,7 +573,7 @@ final class LongVector512 extends LongVector { } // Mask - + @ValueBased static final class LongMask512 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -620,7 +621,7 @@ final class LongVector512 extends LongVector { @Override LongMask512 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -630,7 +631,7 @@ final class LongVector512 extends LongVector { @Override LongMask512 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((LongMask512)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -780,16 +781,16 @@ final class LongVector512 extends LongVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, LongMask512.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((LongMask512)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((LongMask512)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, LongMask512.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((LongMask512)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((LongMask512)m).getBits())); } @ForceInline @@ -797,7 +798,7 @@ final class LongVector512 extends LongVector { static LongMask512 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(LongMask512.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final LongMask512 TRUE_MASK = new LongMask512(true); private static final LongMask512 FALSE_MASK = new LongMask512(false); @@ -805,7 +806,7 @@ final class LongVector512 extends LongVector { } // Shuffle - + @ValueBased static final class LongShuffle512 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -857,7 +858,7 @@ final class LongVector512 extends LongVector { @Override LongVector512 toBitsVector0() { - return ((LongVector512) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((LongVector512) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -931,7 +932,7 @@ final class LongVector512 extends LongVector { @ForceInline public final LongMask512 laneIsValid() { return (LongMask512) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -939,7 +940,7 @@ final class LongVector512 extends LongVector { public final LongShuffle512 rearrange(VectorShuffle shuffle) { LongShuffle512 concreteShuffle = (LongShuffle512) shuffle; return (LongShuffle512) toBitsVector().rearrange(concreteShuffle) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -952,7 +953,7 @@ final class LongVector512 extends LongVector { v = (LongVector512) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (LongShuffle512) v.toShuffle(vspecies(), false); + return (LongShuffle512) v.toShuffle(VSPECIES, false); } private static long[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector64.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector64.java index 5e8451695bc..2a2fe4329a8 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector64.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector64.java @@ -31,16 +31,17 @@ import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class LongVector64 extends LongVector { static final LongSpecies VSPECIES = (LongSpecies) LongVector.SPECIES_64; @@ -366,7 +367,7 @@ final class LongVector64 extends LongVector { @Override @ForceInline public final LongShuffle64 toShuffle() { - return (LongShuffle64) toShuffle(vspecies(), false); + return (LongShuffle64) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -558,7 +559,7 @@ final class LongVector64 extends LongVector { } // Mask - + @ValueBased static final class LongMask64 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -606,7 +607,7 @@ final class LongVector64 extends LongVector { @Override LongMask64 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -616,7 +617,7 @@ final class LongVector64 extends LongVector { @Override LongMask64 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((LongMask64)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -766,16 +767,16 @@ final class LongVector64 extends LongVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, LongMask64.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((LongMask64)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((LongMask64)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, LongMask64.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((LongMask64)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((LongMask64)m).getBits())); } @ForceInline @@ -783,7 +784,7 @@ final class LongVector64 extends LongVector { static LongMask64 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(LongMask64.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final LongMask64 TRUE_MASK = new LongMask64(true); private static final LongMask64 FALSE_MASK = new LongMask64(false); @@ -791,7 +792,7 @@ final class LongVector64 extends LongVector { } // Shuffle - + @ValueBased static final class LongShuffle64 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -843,7 +844,7 @@ final class LongVector64 extends LongVector { @Override LongVector64 toBitsVector0() { - return ((LongVector64) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((LongVector64) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -917,7 +918,7 @@ final class LongVector64 extends LongVector { @ForceInline public final LongMask64 laneIsValid() { return (LongMask64) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -925,7 +926,7 @@ final class LongVector64 extends LongVector { public final LongShuffle64 rearrange(VectorShuffle shuffle) { LongShuffle64 concreteShuffle = (LongShuffle64) shuffle; return (LongShuffle64) toBitsVector().rearrange(concreteShuffle) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -938,7 +939,7 @@ final class LongVector64 extends LongVector { v = (LongVector64) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (LongShuffle64) v.toShuffle(vspecies(), false); + return (LongShuffle64) v.toShuffle(VSPECIES, false); } private static long[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVectorMax.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVectorMax.java index 3469da8f2f4..157c58e20e8 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVectorMax.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVectorMax.java @@ -31,16 +31,17 @@ import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class LongVectorMax extends LongVector { static final LongSpecies VSPECIES = (LongSpecies) LongVector.SPECIES_MAX; @@ -366,7 +367,7 @@ final class LongVectorMax extends LongVector { @Override @ForceInline public final LongShuffleMax toShuffle() { - return (LongShuffleMax) toShuffle(vspecies(), false); + return (LongShuffleMax) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -558,7 +559,7 @@ final class LongVectorMax extends LongVector { } // Mask - + @ValueBased static final class LongMaskMax extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -606,7 +607,7 @@ final class LongVectorMax extends LongVector { @Override LongMaskMax uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -616,7 +617,7 @@ final class LongVectorMax extends LongVector { @Override LongMaskMax bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((LongMaskMax)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -766,16 +767,16 @@ final class LongVectorMax extends LongVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, LongMaskMax.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((LongMaskMax)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((LongMaskMax)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, LongMaskMax.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((LongMaskMax)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((LongMaskMax)m).getBits())); } @ForceInline @@ -783,7 +784,7 @@ final class LongVectorMax extends LongVector { static LongMaskMax maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(LongMaskMax.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final LongMaskMax TRUE_MASK = new LongMaskMax(true); private static final LongMaskMax FALSE_MASK = new LongMaskMax(false); @@ -791,7 +792,7 @@ final class LongVectorMax extends LongVector { } // Shuffle - + @ValueBased static final class LongShuffleMax extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -843,7 +844,7 @@ final class LongVectorMax extends LongVector { @Override LongVectorMax toBitsVector0() { - return ((LongVectorMax) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((LongVectorMax) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -917,7 +918,7 @@ final class LongVectorMax extends LongVector { @ForceInline public final LongMaskMax laneIsValid() { return (LongMaskMax) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -925,7 +926,7 @@ final class LongVectorMax extends LongVector { public final LongShuffleMax rearrange(VectorShuffle shuffle) { LongShuffleMax concreteShuffle = (LongShuffleMax) shuffle; return (LongShuffleMax) toBitsVector().rearrange(concreteShuffle) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -938,7 +939,7 @@ final class LongVectorMax extends LongVector { v = (LongVectorMax) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (LongShuffleMax) v.toShuffle(vspecies(), false); + return (LongShuffleMax) v.toShuffle(VSPECIES, false); } private static long[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector.java index 7ba465706e8..21bc80a12bc 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector.java @@ -49,7 +49,8 @@ import static jdk.incubator.vector.VectorOperators.*; * {@code short} values. */ @SuppressWarnings("cast") // warning: redundant cast -public abstract class ShortVector extends AbstractVector { +public abstract sealed class ShortVector extends AbstractVector + permits ShortVector64, ShortVector128, ShortVector256, ShortVector512, ShortVectorMax { ShortVector(short[] vec) { super(vec); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector128.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector128.java index e989cdbdbea..22bbfce0928 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector128.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector128.java @@ -25,22 +25,22 @@ package jdk.incubator.vector; import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class ShortVector128 extends ShortVector { static final ShortSpecies VSPECIES = (ShortSpecies) ShortVector.SPECIES_128; @@ -371,7 +371,7 @@ final class ShortVector128 extends ShortVector { @Override @ForceInline public final ShortShuffle128 toShuffle() { - return (ShortShuffle128) toShuffle(vspecies(), false); + return (ShortShuffle128) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -582,7 +582,7 @@ final class ShortVector128 extends ShortVector { } // Mask - + @ValueBased static final class ShortMask128 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -630,7 +630,7 @@ final class ShortVector128 extends ShortVector { @Override ShortMask128 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -640,7 +640,7 @@ final class ShortVector128 extends ShortVector { @Override ShortMask128 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((ShortMask128)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -790,16 +790,16 @@ final class ShortVector128 extends ShortVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, ShortMask128.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((ShortMask128)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((ShortMask128)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, ShortMask128.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((ShortMask128)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((ShortMask128)m).getBits())); } @ForceInline @@ -807,7 +807,7 @@ final class ShortVector128 extends ShortVector { static ShortMask128 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(ShortMask128.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final ShortMask128 TRUE_MASK = new ShortMask128(true); private static final ShortMask128 FALSE_MASK = new ShortMask128(false); @@ -815,7 +815,7 @@ final class ShortVector128 extends ShortVector { } // Shuffle - + @ValueBased static final class ShortShuffle128 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -867,7 +867,7 @@ final class ShortVector128 extends ShortVector { @Override ShortVector128 toBitsVector0() { - return ((ShortVector128) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((ShortVector128) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -906,7 +906,7 @@ final class ShortVector128 extends ShortVector { @ForceInline public final ShortMask128 laneIsValid() { return (ShortMask128) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -914,7 +914,7 @@ final class ShortVector128 extends ShortVector { public final ShortShuffle128 rearrange(VectorShuffle shuffle) { ShortShuffle128 concreteShuffle = (ShortShuffle128) shuffle; return (ShortShuffle128) toBitsVector().rearrange(concreteShuffle) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -927,7 +927,7 @@ final class ShortVector128 extends ShortVector { v = (ShortVector128) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (ShortShuffle128) v.toShuffle(vspecies(), false); + return (ShortShuffle128) v.toShuffle(VSPECIES, false); } private static short[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector256.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector256.java index c74188e22f5..6011695bf54 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector256.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector256.java @@ -25,22 +25,22 @@ package jdk.incubator.vector; import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class ShortVector256 extends ShortVector { static final ShortSpecies VSPECIES = (ShortSpecies) ShortVector.SPECIES_256; @@ -371,7 +371,7 @@ final class ShortVector256 extends ShortVector { @Override @ForceInline public final ShortShuffle256 toShuffle() { - return (ShortShuffle256) toShuffle(vspecies(), false); + return (ShortShuffle256) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -598,7 +598,7 @@ final class ShortVector256 extends ShortVector { } // Mask - + @ValueBased static final class ShortMask256 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -646,7 +646,7 @@ final class ShortVector256 extends ShortVector { @Override ShortMask256 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -656,7 +656,7 @@ final class ShortVector256 extends ShortVector { @Override ShortMask256 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((ShortMask256)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -806,16 +806,16 @@ final class ShortVector256 extends ShortVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, ShortMask256.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((ShortMask256)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((ShortMask256)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, ShortMask256.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((ShortMask256)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((ShortMask256)m).getBits())); } @ForceInline @@ -823,7 +823,7 @@ final class ShortVector256 extends ShortVector { static ShortMask256 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(ShortMask256.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final ShortMask256 TRUE_MASK = new ShortMask256(true); private static final ShortMask256 FALSE_MASK = new ShortMask256(false); @@ -831,7 +831,7 @@ final class ShortVector256 extends ShortVector { } // Shuffle - + @ValueBased static final class ShortShuffle256 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -883,7 +883,7 @@ final class ShortVector256 extends ShortVector { @Override ShortVector256 toBitsVector0() { - return ((ShortVector256) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((ShortVector256) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -922,7 +922,7 @@ final class ShortVector256 extends ShortVector { @ForceInline public final ShortMask256 laneIsValid() { return (ShortMask256) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -930,7 +930,7 @@ final class ShortVector256 extends ShortVector { public final ShortShuffle256 rearrange(VectorShuffle shuffle) { ShortShuffle256 concreteShuffle = (ShortShuffle256) shuffle; return (ShortShuffle256) toBitsVector().rearrange(concreteShuffle) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -943,7 +943,7 @@ final class ShortVector256 extends ShortVector { v = (ShortVector256) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (ShortShuffle256) v.toShuffle(vspecies(), false); + return (ShortShuffle256) v.toShuffle(VSPECIES, false); } private static short[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector512.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector512.java index 46b5d652200..e6101d2e6be 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector512.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector512.java @@ -25,22 +25,22 @@ package jdk.incubator.vector; import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class ShortVector512 extends ShortVector { static final ShortSpecies VSPECIES = (ShortSpecies) ShortVector.SPECIES_512; @@ -371,7 +371,7 @@ final class ShortVector512 extends ShortVector { @Override @ForceInline public final ShortShuffle512 toShuffle() { - return (ShortShuffle512) toShuffle(vspecies(), false); + return (ShortShuffle512) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -630,7 +630,7 @@ final class ShortVector512 extends ShortVector { } // Mask - + @ValueBased static final class ShortMask512 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -678,7 +678,7 @@ final class ShortVector512 extends ShortVector { @Override ShortMask512 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -688,7 +688,7 @@ final class ShortVector512 extends ShortVector { @Override ShortMask512 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((ShortMask512)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -838,16 +838,16 @@ final class ShortVector512 extends ShortVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, ShortMask512.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((ShortMask512)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((ShortMask512)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, ShortMask512.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((ShortMask512)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((ShortMask512)m).getBits())); } @ForceInline @@ -855,7 +855,7 @@ final class ShortVector512 extends ShortVector { static ShortMask512 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(ShortMask512.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final ShortMask512 TRUE_MASK = new ShortMask512(true); private static final ShortMask512 FALSE_MASK = new ShortMask512(false); @@ -863,7 +863,7 @@ final class ShortVector512 extends ShortVector { } // Shuffle - + @ValueBased static final class ShortShuffle512 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -915,7 +915,7 @@ final class ShortVector512 extends ShortVector { @Override ShortVector512 toBitsVector0() { - return ((ShortVector512) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((ShortVector512) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -954,7 +954,7 @@ final class ShortVector512 extends ShortVector { @ForceInline public final ShortMask512 laneIsValid() { return (ShortMask512) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -962,7 +962,7 @@ final class ShortVector512 extends ShortVector { public final ShortShuffle512 rearrange(VectorShuffle shuffle) { ShortShuffle512 concreteShuffle = (ShortShuffle512) shuffle; return (ShortShuffle512) toBitsVector().rearrange(concreteShuffle) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -975,7 +975,7 @@ final class ShortVector512 extends ShortVector { v = (ShortVector512) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (ShortShuffle512) v.toShuffle(vspecies(), false); + return (ShortShuffle512) v.toShuffle(VSPECIES, false); } private static short[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector64.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector64.java index 66ff3efe522..31af959b4a8 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector64.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector64.java @@ -25,22 +25,22 @@ package jdk.incubator.vector; import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class ShortVector64 extends ShortVector { static final ShortSpecies VSPECIES = (ShortSpecies) ShortVector.SPECIES_64; @@ -371,7 +371,7 @@ final class ShortVector64 extends ShortVector { @Override @ForceInline public final ShortShuffle64 toShuffle() { - return (ShortShuffle64) toShuffle(vspecies(), false); + return (ShortShuffle64) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -574,7 +574,7 @@ final class ShortVector64 extends ShortVector { } // Mask - + @ValueBased static final class ShortMask64 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -622,7 +622,7 @@ final class ShortVector64 extends ShortVector { @Override ShortMask64 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -632,7 +632,7 @@ final class ShortVector64 extends ShortVector { @Override ShortMask64 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((ShortMask64)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -782,16 +782,16 @@ final class ShortVector64 extends ShortVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, ShortMask64.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((ShortMask64)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((ShortMask64)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, ShortMask64.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((ShortMask64)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((ShortMask64)m).getBits())); } @ForceInline @@ -799,7 +799,7 @@ final class ShortVector64 extends ShortVector { static ShortMask64 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(ShortMask64.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final ShortMask64 TRUE_MASK = new ShortMask64(true); private static final ShortMask64 FALSE_MASK = new ShortMask64(false); @@ -807,7 +807,7 @@ final class ShortVector64 extends ShortVector { } // Shuffle - + @ValueBased static final class ShortShuffle64 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -859,7 +859,7 @@ final class ShortVector64 extends ShortVector { @Override ShortVector64 toBitsVector0() { - return ((ShortVector64) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((ShortVector64) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -898,7 +898,7 @@ final class ShortVector64 extends ShortVector { @ForceInline public final ShortMask64 laneIsValid() { return (ShortMask64) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -906,7 +906,7 @@ final class ShortVector64 extends ShortVector { public final ShortShuffle64 rearrange(VectorShuffle shuffle) { ShortShuffle64 concreteShuffle = (ShortShuffle64) shuffle; return (ShortShuffle64) toBitsVector().rearrange(concreteShuffle) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -919,7 +919,7 @@ final class ShortVector64 extends ShortVector { v = (ShortVector64) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (ShortShuffle64) v.toShuffle(vspecies(), false); + return (ShortShuffle64) v.toShuffle(VSPECIES, false); } private static short[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVectorMax.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVectorMax.java index b9a9b85126b..fe0359c4711 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVectorMax.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVectorMax.java @@ -25,22 +25,22 @@ package jdk.incubator.vector; import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class ShortVectorMax extends ShortVector { static final ShortSpecies VSPECIES = (ShortSpecies) ShortVector.SPECIES_MAX; @@ -371,7 +371,7 @@ final class ShortVectorMax extends ShortVector { @Override @ForceInline public final ShortShuffleMax toShuffle() { - return (ShortShuffleMax) toShuffle(vspecies(), false); + return (ShortShuffleMax) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -568,7 +568,7 @@ final class ShortVectorMax extends ShortVector { } // Mask - + @ValueBased static final class ShortMaskMax extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -616,7 +616,7 @@ final class ShortVectorMax extends ShortVector { @Override ShortMaskMax uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -626,7 +626,7 @@ final class ShortVectorMax extends ShortVector { @Override ShortMaskMax bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((ShortMaskMax)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -776,16 +776,16 @@ final class ShortVectorMax extends ShortVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, ShortMaskMax.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((ShortMaskMax)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((ShortMaskMax)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, ShortMaskMax.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((ShortMaskMax)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((ShortMaskMax)m).getBits())); } @ForceInline @@ -793,7 +793,7 @@ final class ShortVectorMax extends ShortVector { static ShortMaskMax maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(ShortMaskMax.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final ShortMaskMax TRUE_MASK = new ShortMaskMax(true); private static final ShortMaskMax FALSE_MASK = new ShortMaskMax(false); @@ -801,7 +801,7 @@ final class ShortVectorMax extends ShortVector { } // Shuffle - + @ValueBased static final class ShortShuffleMax extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -853,7 +853,7 @@ final class ShortVectorMax extends ShortVector { @Override ShortVectorMax toBitsVector0() { - return ((ShortVectorMax) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((ShortVectorMax) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -892,7 +892,7 @@ final class ShortVectorMax extends ShortVector { @ForceInline public final ShortMaskMax laneIsValid() { return (ShortMaskMax) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -900,7 +900,7 @@ final class ShortVectorMax extends ShortVector { public final ShortShuffleMax rearrange(VectorShuffle shuffle) { ShortShuffleMax concreteShuffle = (ShortShuffleMax) shuffle; return (ShortShuffleMax) toBitsVector().rearrange(concreteShuffle) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -913,7 +913,7 @@ final class ShortVectorMax extends ShortVector { v = (ShortVectorMax) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (ShortShuffleMax) v.toShuffle(vspecies(), false); + return (ShortShuffleMax) v.toShuffle(VSPECIES, false); } private static short[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Util.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Util.java index 8562d4b5d7a..133195fa54d 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Util.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Util.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, 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 @@ -24,7 +24,7 @@ */ package jdk.incubator.vector; -/*package-private*/ class Util { +/*package-private*/ final class Util { public static void requires(boolean cond, String message) { if (!cond) { throw new InternalError(message); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Vector.java index 68b4a35067c..85b3bbca269 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Vector.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, 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 @@ -1170,9 +1170,10 @@ import java.util.Arrays; * @param the boxed version of {@code ETYPE}, * the element type of a vector * + * @sealedGraph */ @SuppressWarnings("exports") -public abstract class Vector extends jdk.internal.vm.vector.VectorSupport.Vector { +public abstract sealed class Vector extends jdk.internal.vm.vector.VectorSupport.Vector permits AbstractVector { // This type is sealed within its package. // Users cannot roll their own vector types. diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorIntrinsics.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorIntrinsics.java index 266a843083a..f0115371d48 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorIntrinsics.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorIntrinsics.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -28,7 +28,7 @@ import jdk.internal.vm.annotation.ForceInline; import java.util.Objects; -/*non-public*/ class VectorIntrinsics { +/*non-public*/ final class VectorIntrinsics { static final int VECTOR_ACCESS_OOB_CHECK = Integer.getInteger("jdk.incubator.vector.VECTOR_ACCESS_OOB_CHECK", 2); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorMask.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorMask.java index 13ee9e27e0d..607b194946b 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorMask.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorMask.java @@ -131,7 +131,7 @@ import java.util.Objects; * the element type of a vector */ @SuppressWarnings("exports") -public abstract class VectorMask extends jdk.internal.vm.vector.VectorSupport.VectorMask { +public abstract sealed class VectorMask extends jdk.internal.vm.vector.VectorSupport.VectorMask permits AbstractMask { VectorMask(boolean[] bits) { super(bits); } /** diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorMathLibrary.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorMathLibrary.java index 1c1cfcc78c7..59697733d86 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorMathLibrary.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorMathLibrary.java @@ -31,6 +31,7 @@ import jdk.internal.vm.vector.VectorSupport; import java.lang.foreign.MemorySegment; import java.lang.foreign.SymbolLookup; +import java.util.Locale; import java.util.function.IntFunction; import static jdk.incubator.vector.Util.requires; @@ -42,7 +43,7 @@ import static jdk.internal.vm.vector.Utils.debug; * A wrapper for native vector math libraries bundled with the JDK (SVML and SLEEF). * Binds vector operations to native implementations provided by the libraries. */ -/*package-private*/ class VectorMathLibrary { +/*package-private*/ final class VectorMathLibrary { private static final SymbolLookup LOOKUP = SymbolLookup.loaderLookup(); interface Library { @@ -141,7 +142,7 @@ import static jdk.internal.vm.vector.Utils.debug; String elemType = (vspecies.elementType() == float.class ? "f" : ""); boolean isFloatVector64 = (vspecies.elementType() == float.class) && (vspecies.length() == 2); // FloatVector64 or FloatVectorMax int vlen = (isFloatVector64 ? 4 : vspecies.length()); // reuse 128-bit variant for 64-bit float vectors - return String.format("__jsvml_%s%s%d_ha_%s", op.operatorName(), elemType, vlen, suffix); + return String.format(Locale.ROOT, "__jsvml_%s%s%d_ha_%s", op.operatorName(), elemType, vlen, suffix); } @Override @@ -214,7 +215,7 @@ import static jdk.internal.vm.vector.Utils.debug; boolean isFloatVector64 = (vspecies.elementType() == float.class) && (vspecies.length() == 2); // FloatVector64 or FloatVectorMax int vlen = (isFloatVector64 ? 4 : vspecies.length()); // reuse 128-bit variant for 64-bit float vectors boolean isShapeAgnostic = isRISCV64() || (isAARCH64() && vspecies.vectorBitSize() > 128); - return String.format("%s%s%s_%s%s", op.operatorName(), + return String.format(Locale.ROOT, "%s%s%s_%s%s", op.operatorName(), (vspecies.elementType() == float.class ? "f" : "d"), (isShapeAgnostic ? "x" : Integer.toString(vlen)), precisionLevel(op), diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorOperators.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorOperators.java index 84009c55ac9..cc5a7ccbdef 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorOperators.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorOperators.java @@ -24,13 +24,12 @@ */ package jdk.incubator.vector; -import java.util.function.IntFunction; -import java.util.HashMap; import java.util.ArrayList; +import java.util.HashMap; +import java.util.function.IntFunction; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.annotation.Stable; - import jdk.internal.vm.vector.VectorSupport; import static jdk.internal.vm.vector.Utils.isNonCapturingLambda; @@ -115,7 +114,7 @@ import static jdk.internal.vm.vector.Utils.isNonCapturingLambda; * operations on individual lane values. * */ -public abstract class VectorOperators { +public final class VectorOperators { private VectorOperators() { } /** @@ -131,12 +130,9 @@ public abstract class VectorOperators { * @see VectorOperators.Test Test * @see VectorOperators.Conversion Conversion * - * @apiNote - * User code should not implement this interface. A future release of - * this type may restrict implementations to be members of the same - * package. + * @sealedGraph */ - public interface Operator { + public sealed interface Operator { /** * Returns the symbolic name of this operator, * as a constant in {@link VectorOperators}. @@ -235,13 +231,8 @@ public abstract class VectorOperators { * usable in expressions like {@code w = v0.}{@link * Vector#lanewise(VectorOperators.Unary) * lanewise}{@code (NEG)}. - * - * @apiNote - * User code should not implement this interface. A future release of - * this type may restrict implementations to be members of the same - * package. */ - public interface Unary extends Operator { + public sealed interface Unary extends Operator { } /** @@ -252,12 +243,9 @@ public abstract class VectorOperators { * Vector#lanewise(VectorOperators.Binary,Vector) * lanewise}{@code (ADD, v1)}. * - * @apiNote - * User code should not implement this interface. A future release of - * this type may restrict implementations to be members of the same - * package. + * @sealedGraph */ - public interface Binary extends Operator { + public sealed interface Binary extends Operator { } /** @@ -267,13 +255,8 @@ public abstract class VectorOperators { * usable in expressions like {@code w = v0.}{@link * Vector#lanewise(VectorOperators.Ternary,Vector,Vector) * lanewise}{@code (FMA, v1, v2)}. - * - * @apiNote - * User code should not implement this interface. A future release of - * this type may restrict implementations to be members of the same - * package. */ - public interface Ternary extends Operator { + public sealed interface Ternary extends Operator { } /** @@ -283,13 +266,8 @@ public abstract class VectorOperators { * usable in expressions like {@code e = v0.}{@link * IntVector#reduceLanes(VectorOperators.Associative) * reduceLanes}{@code (ADD)}. - * - * @apiNote - * User code should not implement this interface. A future release of - * this type may restrict implementations to be members of the same - * package. */ - public interface Associative extends Binary { + public sealed interface Associative extends Binary { } /** @@ -299,13 +277,8 @@ public abstract class VectorOperators { * usable in expressions like {@code m = v0.}{@link * FloatVector#test(VectorOperators.Test) * test}{@code (IS_FINITE)}. - * - * @apiNote - * User code should not implement this interface. A future release of - * this type may restrict implementations to be members of the same - * package. */ - public interface Test extends Operator { + public sealed interface Test extends Operator { } /** @@ -315,13 +288,8 @@ public abstract class VectorOperators { * usable in expressions like {@code m = v0.}{@link * Vector#compare(VectorOperators.Comparison,Vector) * compare}{@code (LT, v1)}. - * - * @apiNote - * User code should not implement this interface. A future release of - * this type may restrict implementations to be members of the same - * package. */ - public interface Comparison extends Operator { + public sealed interface Comparison extends Operator { } /** @@ -336,13 +304,8 @@ public abstract class VectorOperators { * domain type (the input lane type) * @param the boxed element type for the conversion * range type (the output lane type) - * - * @apiNote - * User code should not implement this interface. A future release of - * this type may restrict implementations to be members of the same - * package. */ - public interface Conversion extends Operator { + public sealed interface Conversion extends Operator { /** * The domain of this conversion, a primitive type. * @return the domain of this conversion @@ -597,15 +560,15 @@ public abstract class VectorOperators { /** Produce {@code a<<(n&(ESIZE*8-1))}. Integral only. */ - public static final /*bitwise*/ Binary LSHL = binary("LSHL", "<<", VectorSupport.VECTOR_OP_LSHIFT, VO_SHIFT); + public static final /*bitwise*/ Binary LSHL = binary("LSHL", "<<", VectorSupport.VECTOR_OP_LSHIFT, VO_SHIFT+VO_NOFP); /** Produce {@code a>>(n&(ESIZE*8-1))}. Integral only. */ - public static final /*bitwise*/ Binary ASHR = binary("ASHR", ">>", VectorSupport.VECTOR_OP_RSHIFT, VO_SHIFT); + public static final /*bitwise*/ Binary ASHR = binary("ASHR", ">>", VectorSupport.VECTOR_OP_RSHIFT, VO_SHIFT+VO_NOFP); /** Produce {@code (a&EMASK)>>>(n&(ESIZE*8-1))}. Integral only. */ - public static final /*bitwise*/ Binary LSHR = binary("LSHR", ">>>", VectorSupport.VECTOR_OP_URSHIFT, VO_SHIFT); + public static final /*bitwise*/ Binary LSHR = binary("LSHR", ">>>", VectorSupport.VECTOR_OP_URSHIFT, VO_SHIFT+VO_NOFP); /** Produce {@code rotateLeft(a,n)}. Integral only. */ - public static final /*bitwise*/ Binary ROL = binary("ROL", "rotateLeft", VectorSupport.VECTOR_OP_LROTATE, VO_SHIFT); + public static final /*bitwise*/ Binary ROL = binary("ROL", "rotateLeft", VectorSupport.VECTOR_OP_LROTATE, VO_SHIFT+VO_NOFP); /** Produce {@code rotateRight(a,n)}. Integral only. */ - public static final /*bitwise*/ Binary ROR = binary("ROR", "rotateRight", VectorSupport.VECTOR_OP_RROTATE, VO_SHIFT); + public static final /*bitwise*/ Binary ROR = binary("ROR", "rotateRight", VectorSupport.VECTOR_OP_RROTATE, VO_SHIFT+VO_NOFP); /** Produce {@code compress(a,n)}. Integral, {@code int} and {@code long}, only. * @since 19 */ @@ -831,7 +794,7 @@ public abstract class VectorOperators { kind, dom, ran); } - private abstract static class OperatorImpl implements Operator { + private abstract static sealed class OperatorImpl implements Operator { private final String symName; private final String opName; private final int opInfo; @@ -956,35 +919,35 @@ public abstract class VectorOperators { } } - private static class UnaryImpl extends OperatorImpl implements Unary { + private static final class UnaryImpl extends OperatorImpl implements Unary { private UnaryImpl(String symName, String opName, int opInfo) { super(symName, opName, opInfo); assert((opInfo & VO_ARITY_MASK) == VO_UNARY); } } - private static class BinaryImpl extends OperatorImpl implements Binary { + private static sealed class BinaryImpl extends OperatorImpl implements Binary permits AssociativeImpl { private BinaryImpl(String symName, String opName, int opInfo) { super(symName, opName, opInfo); assert((opInfo & VO_ARITY_MASK) == VO_BINARY); } } - private static class TernaryImpl extends OperatorImpl implements Ternary { + private static final class TernaryImpl extends OperatorImpl implements Ternary { private TernaryImpl(String symName, String opName, int opInfo) { super(symName, opName, opInfo); assert((opInfo & VO_ARITY_MASK) == VO_TERNARY); } } - private static class AssociativeImpl extends BinaryImpl implements Associative { + private static final class AssociativeImpl extends BinaryImpl implements Associative { private AssociativeImpl(String symName, String opName, int opInfo) { super(symName, opName, opInfo); } } /*package-private*/ - static + static final class ConversionImpl extends OperatorImpl implements Conversion { private ConversionImpl(String symName, String opName, int opInfo, @@ -1260,7 +1223,7 @@ public abstract class VectorOperators { } } - private static class TestImpl extends OperatorImpl implements Test { + private static final class TestImpl extends OperatorImpl implements Test { private TestImpl(String symName, String opName, int opInfo) { super(symName, opName, opInfo); assert((opInfo & VO_ARITY_MASK) == VO_UNARY); @@ -1272,7 +1235,7 @@ public abstract class VectorOperators { } } - private static class ComparisonImpl extends OperatorImpl implements Comparison { + private static final class ComparisonImpl extends OperatorImpl implements Comparison { private ComparisonImpl(String symName, String opName, int opInfo) { super(symName, opName, opInfo); assert((opInfo & VO_ARITY_MASK) == VO_BINARY); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorShuffle.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorShuffle.java index 9cde9d2315c..5da38a25e16 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorShuffle.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorShuffle.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, 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,7 @@ import java.util.function.IntUnaryOperator; * the element type of a vector */ @SuppressWarnings("exports") -public abstract class VectorShuffle extends jdk.internal.vm.vector.VectorSupport.VectorShuffle { +public abstract sealed class VectorShuffle extends jdk.internal.vm.vector.VectorSupport.VectorShuffle permits AbstractShuffle { VectorShuffle(Object indices) { super(indices); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorSpecies.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorSpecies.java index e80bbf231ea..4c3ef3f471d 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorSpecies.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorSpecies.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, 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 @@ -36,11 +36,6 @@ import java.util.function.IntUnaryOperator; * of element type ({@code ETYPE}) * and {@link VectorShape shape}. * - * @apiNote - * User code should not implement this interface. A future release of - * this type may restrict implementations to be members of the same - * package. - * * @implNote * The string representation of an instance of this interface will * be of the form "Species[ETYPE, VLENGTH, SHAPE]", where {@code @@ -57,7 +52,7 @@ import java.util.function.IntUnaryOperator; * @param the boxed version of {@code ETYPE}, * the element type of a vector */ -public interface VectorSpecies { +public sealed interface VectorSpecies permits AbstractSpecies { /** * Returns the primitive element type of vectors of this * species. diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-Vector.java.template b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-Vector.java.template index 95fe8ca35db..b3c5bfac302 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-Vector.java.template +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-Vector.java.template @@ -49,7 +49,8 @@ import static jdk.incubator.vector.VectorOperators.*; * {@code $type$} values. */ @SuppressWarnings("cast") // warning: redundant cast -public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { +public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtype$> + permits $Type$Vector64, $Type$Vector128, $Type$Vector256, $Type$Vector512, $Type$VectorMax { $abstractvectortype$($type$[] vec) { super(vec); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-VectorBits.java.template b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-VectorBits.java.template index bbf02f9c6cd..d66d22cab19 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-VectorBits.java.template +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-VectorBits.java.template @@ -25,22 +25,25 @@ package jdk.incubator.vector; import java.lang.foreign.MemorySegment; +#if[longOrDouble] import java.lang.foreign.ValueLayout; +#end[longOrDouble] import java.nio.ByteOrder; import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; #warn This file is preprocessed before being compiled @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class $vectortype$ extends $abstractvectortype$ { static final $Type$Species VSPECIES = ($Type$Species) $Type$Vector.SPECIES_$BITS$; @@ -387,7 +390,7 @@ final class $vectortype$ extends $abstractvectortype$ { @Override @ForceInline public final $shuffletype$ toShuffle() { - return ($shuffletype$) toShuffle(vspecies(), false); + return ($shuffletype$) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -855,7 +858,7 @@ final class $vectortype$ extends $abstractvectortype$ { #end[FP] // Mask - + @ValueBased static final class $masktype$ extends AbstractMask<$Boxtype$> { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -903,7 +906,7 @@ final class $vectortype$ extends $abstractvectortype$ { @Override $masktype$ uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -913,7 +916,7 @@ final class $vectortype$ extends $abstractvectortype$ { @Override $masktype$ bOp(VectorMask<$Boxtype$> m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = (($masktype$)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -1063,16 +1066,16 @@ final class $vectortype$ extends $abstractvectortype$ { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, $masktype$.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper((($masktype$)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper((($masktype$)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, $masktype$.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper((($masktype$)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper((($masktype$)m).getBits())); } @ForceInline @@ -1080,7 +1083,7 @@ final class $vectortype$ extends $abstractvectortype$ { static $masktype$ maskAll(boolean bit) { return VectorSupport.fromBitsCoerced($masktype$.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final $masktype$ TRUE_MASK = new $masktype$(true); private static final $masktype$ FALSE_MASK = new $masktype$(false); @@ -1103,7 +1106,7 @@ final class $vectortype$ extends $abstractvectortype$ { } // Shuffle - + @ValueBased static final class $shuffletype$ extends AbstractShuffle<$Boxtype$> { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -1145,7 +1148,7 @@ final class $vectortype$ extends $abstractvectortype$ { @Override @ForceInline public $vectortype$ toVector() { - return ($vectortype$) toBitsVector().castShape(vspecies(), 0); + return ($vectortype$) toBitsVector().castShape(VSPECIES, 0); } #else[FP] @Override @@ -1163,7 +1166,7 @@ final class $vectortype$ extends $abstractvectortype$ { @Override $bitsvectortype$ toBitsVector0() { - return (($bitsvectortype$) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return (($bitsvectortype$) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -1299,7 +1302,7 @@ final class $vectortype$ extends $abstractvectortype$ { @ForceInline public final $masktype$ laneIsValid() { return ($masktype$) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -1308,10 +1311,10 @@ final class $vectortype$ extends $abstractvectortype$ { $shuffletype$ concreteShuffle = ($shuffletype$) shuffle; #if[FP] return ($shuffletype$) toBitsVector().rearrange(concreteShuffle.cast($Bitstype$Vector.SPECIES_$BITS$)) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); #else[FP] return ($shuffletype$) toBitsVector().rearrange(concreteShuffle) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); #end[FP] } @@ -1325,7 +1328,7 @@ final class $vectortype$ extends $abstractvectortype$ { v = ($bitsvectortype$) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return ($shuffletype$) v.toShuffle(vspecies(), false); + return ($shuffletype$) v.toShuffle(VSPECIES, false); } private static $bitstype$[] prepare(int[] indices, int offset) { diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/aarch64/AArch64.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/aarch64/AArch64.java index 391ac224609..7790a9abd7c 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/aarch64/AArch64.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/aarch64/AArch64.java @@ -184,6 +184,8 @@ public class AArch64 extends Architecture { SVEBITPERM, SVE2, A53MAC, + ECV, + WFXT, FPHP, ASIMDHP, } diff --git a/src/jdk.jartool/share/man/jar.md b/src/jdk.jartool/share/man/jar.md index d944afcfb7f..658fa0cb4fa 100644 --- a/src/jdk.jartool/share/man/jar.md +++ b/src/jdk.jartool/share/man/jar.md @@ -1,5 +1,5 @@ --- -# Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 1997, 2026, 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 @@ -84,29 +84,29 @@ appropriate operation arguments described in this section. You can mix an operation argument with other one-letter options. Generally the operation argument is the first argument specified on the command line. -`-c` or `--create` +[`-c`]{#option--create} or `--create` : Creates the archive. -`-i` *FILE* or `--generate-index=`*FILE* +[`-i`]{#option--generate-index} *FILE* or `--generate-index=`*FILE* : Generates index information for the specified JAR file. This option is deprecated and may be removed in a future release. -`-t` or `--list` +[`-t`]{#option--list} or `--list` : Lists the table of contents for the archive. -`-u` or `--update` +[`-u`]{#option--update} or `--update` : Updates an existing JAR file. -`-x` or `--extract` +[`-x`]{#option--extract} or `--extract` : Extracts the named (or all) files from the archive. If a file with the same name appears more than once in the archive, each copy will be extracted, with later copies overwriting (replacing) earlier copies unless -k is specified. -`-d` or `--describe-module` +[`-d`]{#option--describe-module} or `--describe-module` : Prints the module descriptor or automatic module name. -`--validate` +[`--validate`]{#option--validate} : Validate the contents of the JAR file. See `Integrity of a JAR File` section below for more details. @@ -115,7 +115,7 @@ argument is the first argument specified on the command line. You can use the following options to customize the actions of any operation mode included in the `jar` command. -`-C` *DIR* +[`-C`]{#option-C} *DIR* : When used with the create operation mode, changes the specified directory and includes the *files* specified at the end of the command line. @@ -126,10 +126,10 @@ mode included in the `jar` command. where the JAR file will be extracted. Unlike with the create operation mode, this option can be specified only once with the extract operation mode. -`-f` *FILE* or `--file=`*FILE* +[`-f`]{#option--file} *FILE* or `--file=`*FILE* : Specifies the archive file name. -`--release` *VERSION* +[`--release`]{#option--release} *VERSION* : Creates a multirelease JAR file. Places all files specified after the option into a versioned directory of the JAR file named `META-INF/versions/`*VERSION*`/`, where *VERSION* must be must be a @@ -149,26 +149,26 @@ mode included in the `jar` command. You can use the following options to customize the actions of the create and the update main operation modes: -`-e` *CLASSNAME* or `--main-class=`*CLASSNAME* +[`-e`]{#option--main-class} *CLASSNAME* or `--main-class=`*CLASSNAME* : Specifies the application entry point for standalone applications bundled into a modular or executable modular JAR file. -`-m` *FILE* or `--manifest=`*FILE* +[`-m`]{#option--manifest} *FILE* or `--manifest=`*FILE* : Includes the manifest information from the given manifest file. -`-M` or `--no-manifest` +[`-M`]{#option--no-manifest} or `--no-manifest` : Doesn't create a manifest file for the entries. -`--module-version=`*VERSION* +[`--module-version=`]{#option--module-version}*VERSION* : Specifies the module version, when creating or updating a modular JAR file, or updating a non-modular JAR file. -`--hash-modules=`*PATTERN* +[`--hash-modules=`]{#option--hash-modules}*PATTERN* : Computes and records the hashes of modules matched by the given pattern and that depend upon directly or indirectly on a modular JAR file being created or a non-modular JAR file being updated. -`-p` or `--module-path` +[`-p`]{#option--module-path} or `--module-path` : Specifies the location of module dependence for generating the hash. `@`*file* @@ -181,20 +181,20 @@ You can use the following options to customize the actions of the create (`-c` or `--create`) the update (`-u` or `--update` ) and the generate-index (`-i` or `--generate-index=`*FILE*) main operation modes: -`-0` or `--no-compress` +[`-0`]{#option--no-compress} or `--no-compress` : Stores without using ZIP compression. -`--date=`*TIMESTAMP* +[`--date=`]{#option--date}*TIMESTAMP* : The timestamp in ISO-8601 extended offset date-time with optional time-zone format, to use for the timestamp of the entries, e.g. "2022-02-12T12:30:00-05:00". ## Operation Modifiers Valid Only in Extract Mode -`--dir` *DIR* +[`--dir`]{#option--dir} *DIR* : Directory into which the JAR file will be extracted. -`-k` or `--keep-old-files` +[`-k`]{#option--keep-old-files} or `--keep-old-files` : Do not overwrite existing files. If a Jar file entry with the same name exists in the target directory, the existing file will not be overwritten. diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/doclet/StandardDoclet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/doclet/StandardDoclet.java index 8f5fec1e4b9..90511db8053 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/doclet/StandardDoclet.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/doclet/StandardDoclet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -48,7 +48,7 @@ import jdk.javadoc.internal.doclets.formats.html.HtmlDoclet; * in documentation comments. * * Taglets invoked by the standard doclet must return strings from - * {@link Taglet#toString(List,Element) Taglet.toString} as follows: + * {@link Taglet#toString(List,Element,java.net.URI) Taglet.toString} as follows: * *

*
Inline Tags diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/doclet/Taglet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/doclet/Taglet.java index 1ad67a89fef..ec079047dfa 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/doclet/Taglet.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/doclet/Taglet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, 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 @@ -25,6 +25,7 @@ package jdk.javadoc.doclet; +import java.net.URI; import java.util.List; import java.util.Set; @@ -34,7 +35,7 @@ import com.sun.source.doctree.DocTree; /** * The interface for a custom taglet supported by doclets such as - * the {@link jdk.javadoc.doclet.StandardDoclet standard doclet}. + * the {@linkplain StandardDoclet standard doclet}. * Custom taglets are used to handle custom tags in documentation * comments; custom tags can be instantiated individually as either * block tags, which appear at the end of a comment, @@ -55,10 +56,10 @@ import com.sun.source.doctree.DocTree; * {@link #isInlineTag() isInlineTag}, to determine the characteristics * of the tags supported by the taglet. *
  • As appropriate, the doclet calls the - * {@link #toString(List,Element) toString} method on the taglet object, - * giving it a list of tags and the element for which the tags are part - * of the element's documentation comment, from which the taglet can - * determine the string to be included in the documentation. + * {@link #toString(List,Element,URI) toString} method on the taglet object, + * giving it a list of tags, the element whose documentation comment contains + * the tags, and the root URI of the generated output, from which the taglet + * can determine the string to be included in the documentation. * The doclet will typically specify any requirements on the contents of * the string that is returned. * @@ -126,25 +127,70 @@ public interface Taglet { default void init(DocletEnvironment env, Doclet doclet) { } /** - * Returns the string representation of a series of instances of - * this tag to be included in the generated output. + * Returns the string representation of the specified instances of this tag + * to be included in the generated output. * - *

    If this taglet supports {@link #isInlineTag inline} tags, it will + *

    If this taglet supports {@link #isInlineTag inline} tags, this method will * be called once per instance of the inline tag, each time with a singleton list. * If this taglet supports {@link #isBlockTag block} tags, it will be called once * for each comment containing instances of block tags, with a list of all the instances * of the block tag in that comment. * + * @apiNote Taglets that do not need the root URI of the generated output may + * implement this method only. Taglets that require the root URI to link to other + * doclet-generated resources should override {@link #toString(List, Element, URI)}, + * and optionally throw an exception in the implementation of this method. + * * @param tags the list of instances of this tag * @param element the element to which the enclosing comment belongs * @return the string representation of the tags to be included in * the generated output - * + * @throws UnsupportedOperationException if {@link #toString(List, Element, URI)} + * should be invoked instead * @see User-Defined Taglets * for the Standard Doclet + * @see #toString(List, Element, URI) */ String toString(List tags, Element element); + /** + * Returns the string representation of the specified instances of this tag + * to be included in the generated output. + * + *

    If this taglet supports {@link #isInlineTag inline} tags, this method will + * be called once per instance of the inline tag, each time with a singleton list. + * If this taglet supports {@link #isBlockTag block} tags, it will be called once + * for each comment containing instances of block tags, with a list of all the instances + * of the block tag in that comment. + * + *

    The {@code docRoot} argument identifies the root of the generated output + * as seen by the current resource, and may be used to {@linkplain URI#resolve(String) + * resolve} links to other resources generated by the doclet. + * + * @apiNote The exact form of {@code docRoot} is doclet-specific. For the + * {@linkplain StandardDoclet standard doclet}, it is a relative URI from + * the current resource to the root directory of the generated output. + * Taglets intended for use with other doclets should check the validity + * of the {@code docRoot} argument as appropriate. + * + * @implSpec The default implementation invokes {@link #toString(List, Element) + * toString(tags, element)}. + * + * @param tags the list of instances of this tag + * @param element the element to which the enclosing comment belongs + * @param docRoot the root URI of the generated output + * @return the string representation of the tags to be included in + * the generated output + * @throws IllegalArgumentException if {@code docRoot} is not a valid URI + * @see User-Defined Taglets + * for the Standard Doclet + * @see #toString(List, Element) + * @since 27 + */ + default String toString(List tags, Element element, URI docRoot) { + return toString(tags, element); + } + /** * The kind of location in which a tag may be used. */ diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java index 66fcd90e2e8..17d56ff11ba 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -335,16 +335,9 @@ public class HtmlDoclet extends AbstractDoclet { if (options.createIndex()) { copyResource(DocPaths.SEARCH_JS_TEMPLATE, DocPaths.SCRIPT_FILES.resolve(DocPaths.SEARCH_JS), true); - copyResource(DocPaths.SEARCH_PAGE_JS, DocPaths.SCRIPT_FILES.resolve(DocPaths.SEARCH_PAGE_JS), true); copyResource(DocPaths.GLASS_SVG, DocPaths.RESOURCE_FILES.resolve(DocPaths.GLASS_SVG), false); copyResource(DocPaths.X_SVG, DocPaths.RESOURCE_FILES.resolve(DocPaths.X_SVG), false); - // No newline replacement for JQuery files - copyResource(DocPaths.JQUERY_DIR.resolve(DocPaths.JQUERY_JS), - DocPaths.SCRIPT_FILES.resolve(DocPaths.JQUERY_JS), false); - copyResource(DocPaths.JQUERY_DIR.resolve(DocPaths.JQUERY_UI_JS), - DocPaths.SCRIPT_FILES.resolve(DocPaths.JQUERY_UI_JS), false); - copyResource(DocPaths.JQUERY_DIR.resolve(DocPaths.JQUERY_UI_CSS), - DocPaths.RESOURCE_FILES.resolve(DocPaths.JQUERY_UI_CSS), false); } + } copyLegalFiles(options.createIndex(), options.syntaxHighlight()); // Print a notice if the documentation contains diagnostic markers @@ -369,7 +362,7 @@ public class HtmlDoclet extends AbstractDoclet { case "", "default" -> { // use a known resource as a stand-in, because we cannot get the URL for a resources directory var url = HtmlDoclet.class.getResource( - DocPaths.RESOURCES.resolve(DocPaths.LEGAL).resolve(DocPaths.JQUERY_MD).getPath()); + DocPaths.RESOURCES.resolve(DocPaths.LEGAL).resolve(DocPaths.DEJAVU_MD).getPath()); if (url != null) { try { legalNoticesDir = Path.of(url.toURI()).getParent(); diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java index 86ac3a892fd..594c36c4af2 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, 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 @@ -243,12 +243,8 @@ public abstract class HtmlDocletWriter { if (generating) { writeGenerating(); } - CURRENT_PATH.set(path.getPath()); } - /** Temporary workaround to share current path with taglets, see 8373909 */ - public static final ThreadLocal CURRENT_PATH = new ThreadLocal<>(); - /** * The top-level method to generate and write the page represented by this writer. * diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlIds.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlIds.java index a3fba7eca14..50e6207b833 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlIds.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlIds.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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,6 +112,11 @@ public class HtmlIds { static final HtmlId RELATED_PACKAGE_SUMMARY = HtmlId.of("related-package-summary"); static final HtmlId RESET_SEARCH = HtmlId.of("reset-search"); static final HtmlId SEARCH_INPUT = HtmlId.of("search-input"); + static final HtmlId SEARCH_INPUT_CONTAINER = HtmlId.of("search-input-container"); + static final HtmlId SEARCH_MODULES = HtmlId.of("search-modules"); + static final HtmlId SEARCH_PAGE_LINK = HtmlId.of("search-page-link"); + static final HtmlId SEARCH_RESULT_CONTAINER = HtmlId.of("search-result-container"); + static final HtmlId SEARCH_RESULT_SECTION = HtmlId.of("search-result-section"); static final HtmlId SERVICES = HtmlId.of("services-summary"); static final HtmlId SKIP_NAVBAR_TOP = HtmlId.of("skip-navbar-top"); static final HtmlId THEME_BUTTON = HtmlId.of("theme-button"); diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Navigation.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Navigation.java index 30318bbaeea..cde5b287e25 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Navigation.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Navigation.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -45,6 +45,8 @@ import jdk.javadoc.internal.html.Content; import jdk.javadoc.internal.html.ContentBuilder; import jdk.javadoc.internal.html.Entity; import jdk.javadoc.internal.html.HtmlAttr; +import jdk.javadoc.internal.html.HtmlId; +import jdk.javadoc.internal.html.HtmlTag; import jdk.javadoc.internal.html.HtmlTree; import jdk.javadoc.internal.html.Text; @@ -535,6 +537,42 @@ public class Navigation { .add(inputText) .add(inputReset); target.add(searchDiv); + target.add(HtmlTree.DIV(HtmlIds.SEARCH_RESULT_SECTION) + .add(HtmlTree.DIV(HtmlStyles.searchForm) + .add(HtmlTree.DIV(HtmlTree.LABEL(HtmlIds.SEARCH_INPUT.name(), + contents.getContent("doclet.search.for")))) + .add(HtmlTree.DIV(HtmlIds.SEARCH_INPUT_CONTAINER).addUnchecked(Text.EMPTY)) + .add(createModuleSelector())) + .add(HtmlTree.DIV(HtmlIds.SEARCH_RESULT_CONTAINER).addUnchecked(Text.EMPTY)) + .add(HtmlTree.DIV(HtmlStyles.searchLinks) + .add(HtmlTree.DIV(links.createLink(pathToRoot.resolve(DocPaths.SEARCH_PAGE), + contents.getContent("doclet.search.linkSearchPageLabel")) + .setId(HtmlIds.SEARCH_PAGE_LINK))) + .add(options.noHelp() || !options.helpFile().isEmpty() + ? HtmlTree.DIV(Text.EMPTY).addUnchecked(Text.EMPTY) + : HtmlTree.DIV(links.createLink(pathToRoot.resolve(DocPaths.HELP_DOC).fragment("search"), + contents.getContent("doclet.search.linkSearchHelpLabel")))))); + } + + private Content createModuleSelector() { + if (!configuration.showModules || configuration.modules.size() < 2) { + return Text.EMPTY; + } + var content = new ContentBuilder(HtmlTree.DIV(HtmlTree.LABEL(HtmlIds.SEARCH_MODULES.name(), + contents.getContent("doclet.search.in_modules")))); + var select = HtmlTree.of(HtmlTag.SELECT) + .setId(HtmlIds.SEARCH_MODULES) + .put(HtmlAttr.ARIA_LABEL, configuration.getDocResources().getText("doclet.selectModule")) + .add(HtmlTree.of(HtmlTag.OPTION) + .put(HtmlAttr.VALUE, "") + .add(contents.getContent("doclet.search.all_modules"))); + + for (ModuleElement module : configuration.modules) { + select.add(HtmlTree.of(HtmlTag.OPTION) + .put(HtmlAttr.VALUE, module.getQualifiedName().toString()) + .add(Text.of(module.getQualifiedName().toString()))); + } + return content.add(HtmlTree.DIV(select)); } /** diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SearchWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SearchWriter.java index 433a641530d..5fa0daacf98 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SearchWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SearchWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, 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 @@ -92,17 +92,15 @@ public class SearchWriter extends HtmlDocletWriter { .add(resourceSection) .add(HtmlTree.P(contents.getContent("doclet.search.loading")) .setId(HtmlId.of("page-search-notify"))) - .add(HtmlTree.DIV(HtmlTree.DIV(HtmlId.of("result-container")) + .add(HtmlTree.DIV(HtmlTree.DIV(HtmlIds.SEARCH_RESULT_CONTAINER) .addUnchecked(Text.EMPTY)) - .setId(HtmlId.of("result-section")) - .put(HtmlAttr.STYLE, "display: none;") - .add(HtmlTree.SCRIPT(pathToRoot.resolve(DocPaths.SCRIPT_FILES) - .resolve(DocPaths.SEARCH_PAGE_JS).getPath()))); + .setId(HtmlIds.SEARCH_RESULT_SECTION) + .put(HtmlAttr.STYLE, "display: none;")); } private Content createModuleSelector() { - if (!configuration.showModules) { + if (!configuration.showModules || configuration.modules.size() < 2) { return Text.EMPTY; } @@ -118,7 +116,7 @@ public class SearchWriter extends HtmlDocletWriter { .put(HtmlAttr.VALUE, module.getQualifiedName().toString()) .add(Text.of(module.getQualifiedName().toString()))); } - return new ContentBuilder(contents.getContent("doclet.search.in", select)); + return new ContentBuilder(contents.getContent("doclet.search.in_modules"), select); } private Content createResourceSection() { diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Head.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Head.java index cda4bc9a5be..bff32cbd7bf 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Head.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Head.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -336,11 +336,6 @@ public class Head extends Content { } private void addStylesheets(HtmlTree head) { - if (index) { - // Add JQuery-UI stylesheet first so its rules can be overridden. - addStylesheet(head, DocPaths.RESOURCE_FILES.resolve(DocPaths.JQUERY_UI_CSS)); - } - if (mainStylesheet == null) { mainStylesheet = DocPaths.STYLESHEET; } @@ -381,8 +376,6 @@ public class Head extends Content { .append("loadScripts();\n") .append("initTheme();\n"); } - addScriptElement(head, DocPaths.JQUERY_JS); - addScriptElement(head, DocPaths.JQUERY_UI_JS); } for (HtmlConfiguration.JavaScriptFile javaScriptFile : additionalScripts) { addScriptElement(head, javaScriptFile); diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlStyles.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlStyles.java index 9b59cb0cb47..2b154db7de7 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlStyles.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlStyles.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2026, 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 @@ -734,6 +734,16 @@ public enum HtmlStyles implements HtmlStyle { */ pageSearchInfo, + /** + * The class for a {@code div} element in the search widget containing the search form inputs. + */ + searchForm, + + /** + * The class for a {@code div} element in the search widget containing search-related links. + */ + searchLinks, + /** * The class for a link in the static "Index" pages to a custom searchable item, * such as defined with an {@code @index} tag. diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/jquery/jquery-3.7.1.js b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/jquery/jquery-3.7.1.js deleted file mode 100644 index 1a86433c223..00000000000 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/jquery/jquery-3.7.1.js +++ /dev/null @@ -1,10716 +0,0 @@ -/*! - * jQuery JavaScript Library v3.7.1 - * https://jquery.com/ - * - * Copyright OpenJS Foundation and other contributors - * Released under the MIT license - * https://jquery.org/license - * - * Date: 2023-08-28T13:37Z - */ -( function( global, factory ) { - - "use strict"; - - if ( typeof module === "object" && typeof module.exports === "object" ) { - - // For CommonJS and CommonJS-like environments where a proper `window` - // is present, execute the factory and get jQuery. - // For environments that do not have a `window` with a `document` - // (such as Node.js), expose a factory as module.exports. - // This accentuates the need for the creation of a real `window`. - // e.g. var jQuery = require("jquery")(window); - // See ticket trac-14549 for more info. - module.exports = global.document ? - factory( global, true ) : - function( w ) { - if ( !w.document ) { - throw new Error( "jQuery requires a window with a document" ); - } - return factory( w ); - }; - } else { - factory( global ); - } - -// Pass this if window is not defined yet -} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) { - -// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1 -// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode -// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common -// enough that all such attempts are guarded in a try block. -"use strict"; - -var arr = []; - -var getProto = Object.getPrototypeOf; - -var slice = arr.slice; - -var flat = arr.flat ? function( array ) { - return arr.flat.call( array ); -} : function( array ) { - return arr.concat.apply( [], array ); -}; - - -var push = arr.push; - -var indexOf = arr.indexOf; - -var class2type = {}; - -var toString = class2type.toString; - -var hasOwn = class2type.hasOwnProperty; - -var fnToString = hasOwn.toString; - -var ObjectFunctionString = fnToString.call( Object ); - -var support = {}; - -var isFunction = function isFunction( obj ) { - - // Support: Chrome <=57, Firefox <=52 - // In some browsers, typeof returns "function" for HTML elements - // (i.e., `typeof document.createElement( "object" ) === "function"`). - // We don't want to classify *any* DOM node as a function. - // Support: QtWeb <=3.8.5, WebKit <=534.34, wkhtmltopdf tool <=0.12.5 - // Plus for old WebKit, typeof returns "function" for HTML collections - // (e.g., `typeof document.getElementsByTagName("div") === "function"`). (gh-4756) - return typeof obj === "function" && typeof obj.nodeType !== "number" && - typeof obj.item !== "function"; - }; - - -var isWindow = function isWindow( obj ) { - return obj != null && obj === obj.window; - }; - - -var document = window.document; - - - - var preservedScriptAttributes = { - type: true, - src: true, - nonce: true, - noModule: true - }; - - function DOMEval( code, node, doc ) { - doc = doc || document; - - var i, val, - script = doc.createElement( "script" ); - - script.text = code; - if ( node ) { - for ( i in preservedScriptAttributes ) { - - // Support: Firefox 64+, Edge 18+ - // Some browsers don't support the "nonce" property on scripts. - // On the other hand, just using `getAttribute` is not enough as - // the `nonce` attribute is reset to an empty string whenever it - // becomes browsing-context connected. - // See https://github.com/whatwg/html/issues/2369 - // See https://html.spec.whatwg.org/#nonce-attributes - // The `node.getAttribute` check was added for the sake of - // `jQuery.globalEval` so that it can fake a nonce-containing node - // via an object. - val = node[ i ] || node.getAttribute && node.getAttribute( i ); - if ( val ) { - script.setAttribute( i, val ); - } - } - } - doc.head.appendChild( script ).parentNode.removeChild( script ); - } - - -function toType( obj ) { - if ( obj == null ) { - return obj + ""; - } - - // Support: Android <=2.3 only (functionish RegExp) - return typeof obj === "object" || typeof obj === "function" ? - class2type[ toString.call( obj ) ] || "object" : - typeof obj; -} -/* global Symbol */ -// Defining this global in .eslintrc.json would create a danger of using the global -// unguarded in another place, it seems safer to define global only for this module - - - -var version = "3.7.1", - - rhtmlSuffix = /HTML$/i, - - // Define a local copy of jQuery - jQuery = function( selector, context ) { - - // The jQuery object is actually just the init constructor 'enhanced' - // Need init if jQuery is called (just allow error to be thrown if not included) - return new jQuery.fn.init( selector, context ); - }; - -jQuery.fn = jQuery.prototype = { - - // The current version of jQuery being used - jquery: version, - - constructor: jQuery, - - // The default length of a jQuery object is 0 - length: 0, - - toArray: function() { - return slice.call( this ); - }, - - // Get the Nth element in the matched element set OR - // Get the whole matched element set as a clean array - get: function( num ) { - - // Return all the elements in a clean array - if ( num == null ) { - return slice.call( this ); - } - - // Return just the one element from the set - return num < 0 ? this[ num + this.length ] : this[ num ]; - }, - - // Take an array of elements and push it onto the stack - // (returning the new matched element set) - pushStack: function( elems ) { - - // Build a new jQuery matched element set - var ret = jQuery.merge( this.constructor(), elems ); - - // Add the old object onto the stack (as a reference) - ret.prevObject = this; - - // Return the newly-formed element set - return ret; - }, - - // Execute a callback for every element in the matched set. - each: function( callback ) { - return jQuery.each( this, callback ); - }, - - map: function( callback ) { - return this.pushStack( jQuery.map( this, function( elem, i ) { - return callback.call( elem, i, elem ); - } ) ); - }, - - slice: function() { - return this.pushStack( slice.apply( this, arguments ) ); - }, - - first: function() { - return this.eq( 0 ); - }, - - last: function() { - return this.eq( -1 ); - }, - - even: function() { - return this.pushStack( jQuery.grep( this, function( _elem, i ) { - return ( i + 1 ) % 2; - } ) ); - }, - - odd: function() { - return this.pushStack( jQuery.grep( this, function( _elem, i ) { - return i % 2; - } ) ); - }, - - eq: function( i ) { - var len = this.length, - j = +i + ( i < 0 ? len : 0 ); - return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); - }, - - end: function() { - return this.prevObject || this.constructor(); - }, - - // For internal use only. - // Behaves like an Array's method, not like a jQuery method. - push: push, - sort: arr.sort, - splice: arr.splice -}; - -jQuery.extend = jQuery.fn.extend = function() { - var options, name, src, copy, copyIsArray, clone, - target = arguments[ 0 ] || {}, - i = 1, - length = arguments.length, - deep = false; - - // Handle a deep copy situation - if ( typeof target === "boolean" ) { - deep = target; - - // Skip the boolean and the target - target = arguments[ i ] || {}; - i++; - } - - // Handle case when target is a string or something (possible in deep copy) - if ( typeof target !== "object" && !isFunction( target ) ) { - target = {}; - } - - // Extend jQuery itself if only one argument is passed - if ( i === length ) { - target = this; - i--; - } - - for ( ; i < length; i++ ) { - - // Only deal with non-null/undefined values - if ( ( options = arguments[ i ] ) != null ) { - - // Extend the base object - for ( name in options ) { - copy = options[ name ]; - - // Prevent Object.prototype pollution - // Prevent never-ending loop - if ( name === "__proto__" || target === copy ) { - continue; - } - - // Recurse if we're merging plain objects or arrays - if ( deep && copy && ( jQuery.isPlainObject( copy ) || - ( copyIsArray = Array.isArray( copy ) ) ) ) { - src = target[ name ]; - - // Ensure proper type for the source value - if ( copyIsArray && !Array.isArray( src ) ) { - clone = []; - } else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) { - clone = {}; - } else { - clone = src; - } - copyIsArray = false; - - // Never move original objects, clone them - target[ name ] = jQuery.extend( deep, clone, copy ); - - // Don't bring in undefined values - } else if ( copy !== undefined ) { - target[ name ] = copy; - } - } - } - } - - // Return the modified object - return target; -}; - -jQuery.extend( { - - // Unique for each copy of jQuery on the page - expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), - - // Assume jQuery is ready without the ready module - isReady: true, - - error: function( msg ) { - throw new Error( msg ); - }, - - noop: function() {}, - - isPlainObject: function( obj ) { - var proto, Ctor; - - // Detect obvious negatives - // Use toString instead of jQuery.type to catch host objects - if ( !obj || toString.call( obj ) !== "[object Object]" ) { - return false; - } - - proto = getProto( obj ); - - // Objects with no prototype (e.g., `Object.create( null )`) are plain - if ( !proto ) { - return true; - } - - // Objects with prototype are plain iff they were constructed by a global Object function - Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; - return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; - }, - - isEmptyObject: function( obj ) { - var name; - - for ( name in obj ) { - return false; - } - return true; - }, - - // Evaluates a script in a provided context; falls back to the global one - // if not specified. - globalEval: function( code, options, doc ) { - DOMEval( code, { nonce: options && options.nonce }, doc ); - }, - - each: function( obj, callback ) { - var length, i = 0; - - if ( isArrayLike( obj ) ) { - length = obj.length; - for ( ; i < length; i++ ) { - if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { - break; - } - } - } else { - for ( i in obj ) { - if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { - break; - } - } - } - - return obj; - }, - - - // Retrieve the text value of an array of DOM nodes - text: function( elem ) { - var node, - ret = "", - i = 0, - nodeType = elem.nodeType; - - if ( !nodeType ) { - - // If no nodeType, this is expected to be an array - while ( ( node = elem[ i++ ] ) ) { - - // Do not traverse comment nodes - ret += jQuery.text( node ); - } - } - if ( nodeType === 1 || nodeType === 11 ) { - return elem.textContent; - } - if ( nodeType === 9 ) { - return elem.documentElement.textContent; - } - if ( nodeType === 3 || nodeType === 4 ) { - return elem.nodeValue; - } - - // Do not include comment or processing instruction nodes - - return ret; - }, - - // results is for internal usage only - makeArray: function( arr, results ) { - var ret = results || []; - - if ( arr != null ) { - if ( isArrayLike( Object( arr ) ) ) { - jQuery.merge( ret, - typeof arr === "string" ? - [ arr ] : arr - ); - } else { - push.call( ret, arr ); - } - } - - return ret; - }, - - inArray: function( elem, arr, i ) { - return arr == null ? -1 : indexOf.call( arr, elem, i ); - }, - - isXMLDoc: function( elem ) { - var namespace = elem && elem.namespaceURI, - docElem = elem && ( elem.ownerDocument || elem ).documentElement; - - // Assume HTML when documentElement doesn't yet exist, such as inside - // document fragments. - return !rhtmlSuffix.test( namespace || docElem && docElem.nodeName || "HTML" ); - }, - - // Support: Android <=4.0 only, PhantomJS 1 only - // push.apply(_, arraylike) throws on ancient WebKit - merge: function( first, second ) { - var len = +second.length, - j = 0, - i = first.length; - - for ( ; j < len; j++ ) { - first[ i++ ] = second[ j ]; - } - - first.length = i; - - return first; - }, - - grep: function( elems, callback, invert ) { - var callbackInverse, - matches = [], - i = 0, - length = elems.length, - callbackExpect = !invert; - - // Go through the array, only saving the items - // that pass the validator function - for ( ; i < length; i++ ) { - callbackInverse = !callback( elems[ i ], i ); - if ( callbackInverse !== callbackExpect ) { - matches.push( elems[ i ] ); - } - } - - return matches; - }, - - // arg is for internal usage only - map: function( elems, callback, arg ) { - var length, value, - i = 0, - ret = []; - - // Go through the array, translating each of the items to their new values - if ( isArrayLike( elems ) ) { - length = elems.length; - for ( ; i < length; i++ ) { - value = callback( elems[ i ], i, arg ); - - if ( value != null ) { - ret.push( value ); - } - } - - // Go through every key on the object, - } else { - for ( i in elems ) { - value = callback( elems[ i ], i, arg ); - - if ( value != null ) { - ret.push( value ); - } - } - } - - // Flatten any nested arrays - return flat( ret ); - }, - - // A global GUID counter for objects - guid: 1, - - // jQuery.support is not used in Core but other projects attach their - // properties to it so it needs to exist. - support: support -} ); - -if ( typeof Symbol === "function" ) { - jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; -} - -// Populate the class2type map -jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), - function( _i, name ) { - class2type[ "[object " + name + "]" ] = name.toLowerCase(); - } ); - -function isArrayLike( obj ) { - - // Support: real iOS 8.2 only (not reproducible in simulator) - // `in` check used to prevent JIT error (gh-2145) - // hasOwn isn't used here due to false negatives - // regarding Nodelist length in IE - var length = !!obj && "length" in obj && obj.length, - type = toType( obj ); - - if ( isFunction( obj ) || isWindow( obj ) ) { - return false; - } - - return type === "array" || length === 0 || - typeof length === "number" && length > 0 && ( length - 1 ) in obj; -} - - -function nodeName( elem, name ) { - - return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); - -} -var pop = arr.pop; - - -var sort = arr.sort; - - -var splice = arr.splice; - - -var whitespace = "[\\x20\\t\\r\\n\\f]"; - - -var rtrimCSS = new RegExp( - "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", - "g" -); - - - - -// Note: an element does not contain itself -jQuery.contains = function( a, b ) { - var bup = b && b.parentNode; - - return a === bup || !!( bup && bup.nodeType === 1 && ( - - // Support: IE 9 - 11+ - // IE doesn't have `contains` on SVG. - a.contains ? - a.contains( bup ) : - a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 - ) ); -}; - - - - -// CSS string/identifier serialization -// https://drafts.csswg.org/cssom/#common-serializing-idioms -var rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g; - -function fcssescape( ch, asCodePoint ) { - if ( asCodePoint ) { - - // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER - if ( ch === "\0" ) { - return "\uFFFD"; - } - - // Control characters and (dependent upon position) numbers get escaped as code points - return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; - } - - // Other potentially-special ASCII characters get backslash-escaped - return "\\" + ch; -} - -jQuery.escapeSelector = function( sel ) { - return ( sel + "" ).replace( rcssescape, fcssescape ); -}; - - - - -var preferredDoc = document, - pushNative = push; - -( function() { - -var i, - Expr, - outermostContext, - sortInput, - hasDuplicate, - push = pushNative, - - // Local document vars - document, - documentElement, - documentIsHTML, - rbuggyQSA, - matches, - - // Instance-specific data - expando = jQuery.expando, - dirruns = 0, - done = 0, - classCache = createCache(), - tokenCache = createCache(), - compilerCache = createCache(), - nonnativeSelectorCache = createCache(), - sortOrder = function( a, b ) { - if ( a === b ) { - hasDuplicate = true; - } - return 0; - }, - - booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|" + - "loop|multiple|open|readonly|required|scoped", - - // Regular expressions - - // https://www.w3.org/TR/css-syntax-3/#ident-token-diagram - identifier = "(?:\\\\[\\da-fA-F]{1,6}" + whitespace + - "?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+", - - // Attribute selectors: https://www.w3.org/TR/selectors/#attribute-selectors - attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + - - // Operator (capture 2) - "*([*^$|!~]?=)" + whitespace + - - // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" - "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + - whitespace + "*\\]", - - pseudos = ":(" + identifier + ")(?:\\((" + - - // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: - // 1. quoted (capture 3; capture 4 or capture 5) - "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + - - // 2. simple (capture 6) - "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + - - // 3. anything else (capture 2) - ".*" + - ")\\)|)", - - // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter - rwhitespace = new RegExp( whitespace + "+", "g" ), - - rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), - rleadingCombinator = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + - whitespace + "*" ), - rdescend = new RegExp( whitespace + "|>" ), - - rpseudo = new RegExp( pseudos ), - ridentifier = new RegExp( "^" + identifier + "$" ), - - matchExpr = { - ID: new RegExp( "^#(" + identifier + ")" ), - CLASS: new RegExp( "^\\.(" + identifier + ")" ), - TAG: new RegExp( "^(" + identifier + "|[*])" ), - ATTR: new RegExp( "^" + attributes ), - PSEUDO: new RegExp( "^" + pseudos ), - CHILD: new RegExp( - "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + - whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + - whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), - bool: new RegExp( "^(?:" + booleans + ")$", "i" ), - - // For use in libraries implementing .is() - // We use this for POS matching in `select` - needsContext: new RegExp( "^" + whitespace + - "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace + - "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) - }, - - rinputs = /^(?:input|select|textarea|button)$/i, - rheader = /^h\d$/i, - - // Easily-parseable/retrievable ID or TAG or CLASS selectors - rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, - - rsibling = /[+~]/, - - // CSS escapes - // https://www.w3.org/TR/CSS21/syndata.html#escaped-characters - runescape = new RegExp( "\\\\[\\da-fA-F]{1,6}" + whitespace + - "?|\\\\([^\\r\\n\\f])", "g" ), - funescape = function( escape, nonHex ) { - var high = "0x" + escape.slice( 1 ) - 0x10000; - - if ( nonHex ) { - - // Strip the backslash prefix from a non-hex escape sequence - return nonHex; - } - - // Replace a hexadecimal escape sequence with the encoded Unicode code point - // Support: IE <=11+ - // For values outside the Basic Multilingual Plane (BMP), manually construct a - // surrogate pair - return high < 0 ? - String.fromCharCode( high + 0x10000 ) : - String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); - }, - - // Used for iframes; see `setDocument`. - // Support: IE 9 - 11+, Edge 12 - 18+ - // Removing the function wrapper causes a "Permission Denied" - // error in IE/Edge. - unloadHandler = function() { - setDocument(); - }, - - inDisabledFieldset = addCombinator( - function( elem ) { - return elem.disabled === true && nodeName( elem, "fieldset" ); - }, - { dir: "parentNode", next: "legend" } - ); - -// Support: IE <=9 only -// Accessing document.activeElement can throw unexpectedly -// https://bugs.jquery.com/ticket/13393 -function safeActiveElement() { - try { - return document.activeElement; - } catch ( err ) { } -} - -// Optimize for push.apply( _, NodeList ) -try { - push.apply( - ( arr = slice.call( preferredDoc.childNodes ) ), - preferredDoc.childNodes - ); - - // Support: Android <=4.0 - // Detect silently failing push.apply - // eslint-disable-next-line no-unused-expressions - arr[ preferredDoc.childNodes.length ].nodeType; -} catch ( e ) { - push = { - apply: function( target, els ) { - pushNative.apply( target, slice.call( els ) ); - }, - call: function( target ) { - pushNative.apply( target, slice.call( arguments, 1 ) ); - } - }; -} - -function find( selector, context, results, seed ) { - var m, i, elem, nid, match, groups, newSelector, - newContext = context && context.ownerDocument, - - // nodeType defaults to 9, since context defaults to document - nodeType = context ? context.nodeType : 9; - - results = results || []; - - // Return early from calls with invalid selector or context - if ( typeof selector !== "string" || !selector || - nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { - - return results; - } - - // Try to shortcut find operations (as opposed to filters) in HTML documents - if ( !seed ) { - setDocument( context ); - context = context || document; - - if ( documentIsHTML ) { - - // If the selector is sufficiently simple, try using a "get*By*" DOM method - // (excepting DocumentFragment context, where the methods don't exist) - if ( nodeType !== 11 && ( match = rquickExpr.exec( selector ) ) ) { - - // ID selector - if ( ( m = match[ 1 ] ) ) { - - // Document context - if ( nodeType === 9 ) { - if ( ( elem = context.getElementById( m ) ) ) { - - // Support: IE 9 only - // getElementById can match elements by name instead of ID - if ( elem.id === m ) { - push.call( results, elem ); - return results; - } - } else { - return results; - } - - // Element context - } else { - - // Support: IE 9 only - // getElementById can match elements by name instead of ID - if ( newContext && ( elem = newContext.getElementById( m ) ) && - find.contains( context, elem ) && - elem.id === m ) { - - push.call( results, elem ); - return results; - } - } - - // Type selector - } else if ( match[ 2 ] ) { - push.apply( results, context.getElementsByTagName( selector ) ); - return results; - - // Class selector - } else if ( ( m = match[ 3 ] ) && context.getElementsByClassName ) { - push.apply( results, context.getElementsByClassName( m ) ); - return results; - } - } - - // Take advantage of querySelectorAll - if ( !nonnativeSelectorCache[ selector + " " ] && - ( !rbuggyQSA || !rbuggyQSA.test( selector ) ) ) { - - newSelector = selector; - newContext = context; - - // qSA considers elements outside a scoping root when evaluating child or - // descendant combinators, which is not what we want. - // In such cases, we work around the behavior by prefixing every selector in the - // list with an ID selector referencing the scope context. - // The technique has to be used as well when a leading combinator is used - // as such selectors are not recognized by querySelectorAll. - // Thanks to Andrew Dupont for this technique. - if ( nodeType === 1 && - ( rdescend.test( selector ) || rleadingCombinator.test( selector ) ) ) { - - // Expand context for sibling selectors - newContext = rsibling.test( selector ) && testContext( context.parentNode ) || - context; - - // We can use :scope instead of the ID hack if the browser - // supports it & if we're not changing the context. - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when - // strict-comparing two documents; shallow comparisons work. - // eslint-disable-next-line eqeqeq - if ( newContext != context || !support.scope ) { - - // Capture the context ID, setting it first if necessary - if ( ( nid = context.getAttribute( "id" ) ) ) { - nid = jQuery.escapeSelector( nid ); - } else { - context.setAttribute( "id", ( nid = expando ) ); - } - } - - // Prefix every selector in the list - groups = tokenize( selector ); - i = groups.length; - while ( i-- ) { - groups[ i ] = ( nid ? "#" + nid : ":scope" ) + " " + - toSelector( groups[ i ] ); - } - newSelector = groups.join( "," ); - } - - try { - push.apply( results, - newContext.querySelectorAll( newSelector ) - ); - return results; - } catch ( qsaError ) { - nonnativeSelectorCache( selector, true ); - } finally { - if ( nid === expando ) { - context.removeAttribute( "id" ); - } - } - } - } - } - - // All others - return select( selector.replace( rtrimCSS, "$1" ), context, results, seed ); -} - -/** - * Create key-value caches of limited size - * @returns {function(string, object)} Returns the Object data after storing it on itself with - * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) - * deleting the oldest entry - */ -function createCache() { - var keys = []; - - function cache( key, value ) { - - // Use (key + " ") to avoid collision with native prototype properties - // (see https://github.com/jquery/sizzle/issues/157) - if ( keys.push( key + " " ) > Expr.cacheLength ) { - - // Only keep the most recent entries - delete cache[ keys.shift() ]; - } - return ( cache[ key + " " ] = value ); - } - return cache; -} - -/** - * Mark a function for special use by jQuery selector module - * @param {Function} fn The function to mark - */ -function markFunction( fn ) { - fn[ expando ] = true; - return fn; -} - -/** - * Support testing using an element - * @param {Function} fn Passed the created element and returns a boolean result - */ -function assert( fn ) { - var el = document.createElement( "fieldset" ); - - try { - return !!fn( el ); - } catch ( e ) { - return false; - } finally { - - // Remove from its parent by default - if ( el.parentNode ) { - el.parentNode.removeChild( el ); - } - - // release memory in IE - el = null; - } -} - -/** - * Returns a function to use in pseudos for input types - * @param {String} type - */ -function createInputPseudo( type ) { - return function( elem ) { - return nodeName( elem, "input" ) && elem.type === type; - }; -} - -/** - * Returns a function to use in pseudos for buttons - * @param {String} type - */ -function createButtonPseudo( type ) { - return function( elem ) { - return ( nodeName( elem, "input" ) || nodeName( elem, "button" ) ) && - elem.type === type; - }; -} - -/** - * Returns a function to use in pseudos for :enabled/:disabled - * @param {Boolean} disabled true for :disabled; false for :enabled - */ -function createDisabledPseudo( disabled ) { - - // Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable - return function( elem ) { - - // Only certain elements can match :enabled or :disabled - // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled - // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled - if ( "form" in elem ) { - - // Check for inherited disabledness on relevant non-disabled elements: - // * listed form-associated elements in a disabled fieldset - // https://html.spec.whatwg.org/multipage/forms.html#category-listed - // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled - // * option elements in a disabled optgroup - // https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled - // All such elements have a "form" property. - if ( elem.parentNode && elem.disabled === false ) { - - // Option elements defer to a parent optgroup if present - if ( "label" in elem ) { - if ( "label" in elem.parentNode ) { - return elem.parentNode.disabled === disabled; - } else { - return elem.disabled === disabled; - } - } - - // Support: IE 6 - 11+ - // Use the isDisabled shortcut property to check for disabled fieldset ancestors - return elem.isDisabled === disabled || - - // Where there is no isDisabled, check manually - elem.isDisabled !== !disabled && - inDisabledFieldset( elem ) === disabled; - } - - return elem.disabled === disabled; - - // Try to winnow out elements that can't be disabled before trusting the disabled property. - // Some victims get caught in our net (label, legend, menu, track), but it shouldn't - // even exist on them, let alone have a boolean value. - } else if ( "label" in elem ) { - return elem.disabled === disabled; - } - - // Remaining elements are neither :enabled nor :disabled - return false; - }; -} - -/** - * Returns a function to use in pseudos for positionals - * @param {Function} fn - */ -function createPositionalPseudo( fn ) { - return markFunction( function( argument ) { - argument = +argument; - return markFunction( function( seed, matches ) { - var j, - matchIndexes = fn( [], seed.length, argument ), - i = matchIndexes.length; - - // Match elements found at the specified indexes - while ( i-- ) { - if ( seed[ ( j = matchIndexes[ i ] ) ] ) { - seed[ j ] = !( matches[ j ] = seed[ j ] ); - } - } - } ); - } ); -} - -/** - * Checks a node for validity as a jQuery selector context - * @param {Element|Object=} context - * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value - */ -function testContext( context ) { - return context && typeof context.getElementsByTagName !== "undefined" && context; -} - -/** - * Sets document-related variables once based on the current document - * @param {Element|Object} [node] An element or document object to use to set the document - * @returns {Object} Returns the current document - */ -function setDocument( node ) { - var subWindow, - doc = node ? node.ownerDocument || node : preferredDoc; - - // Return early if doc is invalid or already selected - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when strict-comparing - // two documents; shallow comparisons work. - // eslint-disable-next-line eqeqeq - if ( doc == document || doc.nodeType !== 9 || !doc.documentElement ) { - return document; - } - - // Update global variables - document = doc; - documentElement = document.documentElement; - documentIsHTML = !jQuery.isXMLDoc( document ); - - // Support: iOS 7 only, IE 9 - 11+ - // Older browsers didn't support unprefixed `matches`. - matches = documentElement.matches || - documentElement.webkitMatchesSelector || - documentElement.msMatchesSelector; - - // Support: IE 9 - 11+, Edge 12 - 18+ - // Accessing iframe documents after unload throws "permission denied" errors - // (see trac-13936). - // Limit the fix to IE & Edge Legacy; despite Edge 15+ implementing `matches`, - // all IE 9+ and Edge Legacy versions implement `msMatchesSelector` as well. - if ( documentElement.msMatchesSelector && - - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when strict-comparing - // two documents; shallow comparisons work. - // eslint-disable-next-line eqeqeq - preferredDoc != document && - ( subWindow = document.defaultView ) && subWindow.top !== subWindow ) { - - // Support: IE 9 - 11+, Edge 12 - 18+ - subWindow.addEventListener( "unload", unloadHandler ); - } - - // Support: IE <10 - // Check if getElementById returns elements by name - // The broken getElementById methods don't pick up programmatically-set names, - // so use a roundabout getElementsByName test - support.getById = assert( function( el ) { - documentElement.appendChild( el ).id = jQuery.expando; - return !document.getElementsByName || - !document.getElementsByName( jQuery.expando ).length; - } ); - - // Support: IE 9 only - // Check to see if it's possible to do matchesSelector - // on a disconnected node. - support.disconnectedMatch = assert( function( el ) { - return matches.call( el, "*" ); - } ); - - // Support: IE 9 - 11+, Edge 12 - 18+ - // IE/Edge don't support the :scope pseudo-class. - support.scope = assert( function() { - return document.querySelectorAll( ":scope" ); - } ); - - // Support: Chrome 105 - 111 only, Safari 15.4 - 16.3 only - // Make sure the `:has()` argument is parsed unforgivingly. - // We include `*` in the test to detect buggy implementations that are - // _selectively_ forgiving (specifically when the list includes at least - // one valid selector). - // Note that we treat complete lack of support for `:has()` as if it were - // spec-compliant support, which is fine because use of `:has()` in such - // environments will fail in the qSA path and fall back to jQuery traversal - // anyway. - support.cssHas = assert( function() { - try { - document.querySelector( ":has(*,:jqfake)" ); - return false; - } catch ( e ) { - return true; - } - } ); - - // ID filter and find - if ( support.getById ) { - Expr.filter.ID = function( id ) { - var attrId = id.replace( runescape, funescape ); - return function( elem ) { - return elem.getAttribute( "id" ) === attrId; - }; - }; - Expr.find.ID = function( id, context ) { - if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { - var elem = context.getElementById( id ); - return elem ? [ elem ] : []; - } - }; - } else { - Expr.filter.ID = function( id ) { - var attrId = id.replace( runescape, funescape ); - return function( elem ) { - var node = typeof elem.getAttributeNode !== "undefined" && - elem.getAttributeNode( "id" ); - return node && node.value === attrId; - }; - }; - - // Support: IE 6 - 7 only - // getElementById is not reliable as a find shortcut - Expr.find.ID = function( id, context ) { - if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { - var node, i, elems, - elem = context.getElementById( id ); - - if ( elem ) { - - // Verify the id attribute - node = elem.getAttributeNode( "id" ); - if ( node && node.value === id ) { - return [ elem ]; - } - - // Fall back on getElementsByName - elems = context.getElementsByName( id ); - i = 0; - while ( ( elem = elems[ i++ ] ) ) { - node = elem.getAttributeNode( "id" ); - if ( node && node.value === id ) { - return [ elem ]; - } - } - } - - return []; - } - }; - } - - // Tag - Expr.find.TAG = function( tag, context ) { - if ( typeof context.getElementsByTagName !== "undefined" ) { - return context.getElementsByTagName( tag ); - - // DocumentFragment nodes don't have gEBTN - } else { - return context.querySelectorAll( tag ); - } - }; - - // Class - Expr.find.CLASS = function( className, context ) { - if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) { - return context.getElementsByClassName( className ); - } - }; - - /* QSA/matchesSelector - ---------------------------------------------------------------------- */ - - // QSA and matchesSelector support - - rbuggyQSA = []; - - // Build QSA regex - // Regex strategy adopted from Diego Perini - assert( function( el ) { - - var input; - - documentElement.appendChild( el ).innerHTML = - "" + - ""; - - // Support: iOS <=7 - 8 only - // Boolean attributes and "value" are not treated correctly in some XML documents - if ( !el.querySelectorAll( "[selected]" ).length ) { - rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); - } - - // Support: iOS <=7 - 8 only - if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) { - rbuggyQSA.push( "~=" ); - } - - // Support: iOS 8 only - // https://bugs.webkit.org/show_bug.cgi?id=136851 - // In-page `selector#id sibling-combinator selector` fails - if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) { - rbuggyQSA.push( ".#.+[+~]" ); - } - - // Support: Chrome <=105+, Firefox <=104+, Safari <=15.4+ - // In some of the document kinds, these selectors wouldn't work natively. - // This is probably OK but for backwards compatibility we want to maintain - // handling them through jQuery traversal in jQuery 3.x. - if ( !el.querySelectorAll( ":checked" ).length ) { - rbuggyQSA.push( ":checked" ); - } - - // Support: Windows 8 Native Apps - // The type and name attributes are restricted during .innerHTML assignment - input = document.createElement( "input" ); - input.setAttribute( "type", "hidden" ); - el.appendChild( input ).setAttribute( "name", "D" ); - - // Support: IE 9 - 11+ - // IE's :disabled selector does not pick up the children of disabled fieldsets - // Support: Chrome <=105+, Firefox <=104+, Safari <=15.4+ - // In some of the document kinds, these selectors wouldn't work natively. - // This is probably OK but for backwards compatibility we want to maintain - // handling them through jQuery traversal in jQuery 3.x. - documentElement.appendChild( el ).disabled = true; - if ( el.querySelectorAll( ":disabled" ).length !== 2 ) { - rbuggyQSA.push( ":enabled", ":disabled" ); - } - - // Support: IE 11+, Edge 15 - 18+ - // IE 11/Edge don't find elements on a `[name='']` query in some cases. - // Adding a temporary attribute to the document before the selection works - // around the issue. - // Interestingly, IE 10 & older don't seem to have the issue. - input = document.createElement( "input" ); - input.setAttribute( "name", "" ); - el.appendChild( input ); - if ( !el.querySelectorAll( "[name='']" ).length ) { - rbuggyQSA.push( "\\[" + whitespace + "*name" + whitespace + "*=" + - whitespace + "*(?:''|\"\")" ); - } - } ); - - if ( !support.cssHas ) { - - // Support: Chrome 105 - 110+, Safari 15.4 - 16.3+ - // Our regular `try-catch` mechanism fails to detect natively-unsupported - // pseudo-classes inside `:has()` (such as `:has(:contains("Foo"))`) - // in browsers that parse the `:has()` argument as a forgiving selector list. - // https://drafts.csswg.org/selectors/#relational now requires the argument - // to be parsed unforgivingly, but browsers have not yet fully adjusted. - rbuggyQSA.push( ":has" ); - } - - rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join( "|" ) ); - - /* Sorting - ---------------------------------------------------------------------- */ - - // Document order sorting - sortOrder = function( a, b ) { - - // Flag for duplicate removal - if ( a === b ) { - hasDuplicate = true; - return 0; - } - - // Sort on method existence if only one input has compareDocumentPosition - var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; - if ( compare ) { - return compare; - } - - // Calculate position if both inputs belong to the same document - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when strict-comparing - // two documents; shallow comparisons work. - // eslint-disable-next-line eqeqeq - compare = ( a.ownerDocument || a ) == ( b.ownerDocument || b ) ? - a.compareDocumentPosition( b ) : - - // Otherwise we know they are disconnected - 1; - - // Disconnected nodes - if ( compare & 1 || - ( !support.sortDetached && b.compareDocumentPosition( a ) === compare ) ) { - - // Choose the first element that is related to our preferred document - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when strict-comparing - // two documents; shallow comparisons work. - // eslint-disable-next-line eqeqeq - if ( a === document || a.ownerDocument == preferredDoc && - find.contains( preferredDoc, a ) ) { - return -1; - } - - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when strict-comparing - // two documents; shallow comparisons work. - // eslint-disable-next-line eqeqeq - if ( b === document || b.ownerDocument == preferredDoc && - find.contains( preferredDoc, b ) ) { - return 1; - } - - // Maintain original order - return sortInput ? - ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : - 0; - } - - return compare & 4 ? -1 : 1; - }; - - return document; -} - -find.matches = function( expr, elements ) { - return find( expr, null, null, elements ); -}; - -find.matchesSelector = function( elem, expr ) { - setDocument( elem ); - - if ( documentIsHTML && - !nonnativeSelectorCache[ expr + " " ] && - ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { - - try { - var ret = matches.call( elem, expr ); - - // IE 9's matchesSelector returns false on disconnected nodes - if ( ret || support.disconnectedMatch || - - // As well, disconnected nodes are said to be in a document - // fragment in IE 9 - elem.document && elem.document.nodeType !== 11 ) { - return ret; - } - } catch ( e ) { - nonnativeSelectorCache( expr, true ); - } - } - - return find( expr, document, null, [ elem ] ).length > 0; -}; - -find.contains = function( context, elem ) { - - // Set document vars if needed - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when strict-comparing - // two documents; shallow comparisons work. - // eslint-disable-next-line eqeqeq - if ( ( context.ownerDocument || context ) != document ) { - setDocument( context ); - } - return jQuery.contains( context, elem ); -}; - - -find.attr = function( elem, name ) { - - // Set document vars if needed - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when strict-comparing - // two documents; shallow comparisons work. - // eslint-disable-next-line eqeqeq - if ( ( elem.ownerDocument || elem ) != document ) { - setDocument( elem ); - } - - var fn = Expr.attrHandle[ name.toLowerCase() ], - - // Don't get fooled by Object.prototype properties (see trac-13807) - val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? - fn( elem, name, !documentIsHTML ) : - undefined; - - if ( val !== undefined ) { - return val; - } - - return elem.getAttribute( name ); -}; - -find.error = function( msg ) { - throw new Error( "Syntax error, unrecognized expression: " + msg ); -}; - -/** - * Document sorting and removing duplicates - * @param {ArrayLike} results - */ -jQuery.uniqueSort = function( results ) { - var elem, - duplicates = [], - j = 0, - i = 0; - - // Unless we *know* we can detect duplicates, assume their presence - // - // Support: Android <=4.0+ - // Testing for detecting duplicates is unpredictable so instead assume we can't - // depend on duplicate detection in all browsers without a stable sort. - hasDuplicate = !support.sortStable; - sortInput = !support.sortStable && slice.call( results, 0 ); - sort.call( results, sortOrder ); - - if ( hasDuplicate ) { - while ( ( elem = results[ i++ ] ) ) { - if ( elem === results[ i ] ) { - j = duplicates.push( i ); - } - } - while ( j-- ) { - splice.call( results, duplicates[ j ], 1 ); - } - } - - // Clear input after sorting to release objects - // See https://github.com/jquery/sizzle/pull/225 - sortInput = null; - - return results; -}; - -jQuery.fn.uniqueSort = function() { - return this.pushStack( jQuery.uniqueSort( slice.apply( this ) ) ); -}; - -Expr = jQuery.expr = { - - // Can be adjusted by the user - cacheLength: 50, - - createPseudo: markFunction, - - match: matchExpr, - - attrHandle: {}, - - find: {}, - - relative: { - ">": { dir: "parentNode", first: true }, - " ": { dir: "parentNode" }, - "+": { dir: "previousSibling", first: true }, - "~": { dir: "previousSibling" } - }, - - preFilter: { - ATTR: function( match ) { - match[ 1 ] = match[ 1 ].replace( runescape, funescape ); - - // Move the given value to match[3] whether quoted or unquoted - match[ 3 ] = ( match[ 3 ] || match[ 4 ] || match[ 5 ] || "" ) - .replace( runescape, funescape ); - - if ( match[ 2 ] === "~=" ) { - match[ 3 ] = " " + match[ 3 ] + " "; - } - - return match.slice( 0, 4 ); - }, - - CHILD: function( match ) { - - /* matches from matchExpr["CHILD"] - 1 type (only|nth|...) - 2 what (child|of-type) - 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) - 4 xn-component of xn+y argument ([+-]?\d*n|) - 5 sign of xn-component - 6 x of xn-component - 7 sign of y-component - 8 y of y-component - */ - match[ 1 ] = match[ 1 ].toLowerCase(); - - if ( match[ 1 ].slice( 0, 3 ) === "nth" ) { - - // nth-* requires argument - if ( !match[ 3 ] ) { - find.error( match[ 0 ] ); - } - - // numeric x and y parameters for Expr.filter.CHILD - // remember that false/true cast respectively to 0/1 - match[ 4 ] = +( match[ 4 ] ? - match[ 5 ] + ( match[ 6 ] || 1 ) : - 2 * ( match[ 3 ] === "even" || match[ 3 ] === "odd" ) - ); - match[ 5 ] = +( ( match[ 7 ] + match[ 8 ] ) || match[ 3 ] === "odd" ); - - // other types prohibit arguments - } else if ( match[ 3 ] ) { - find.error( match[ 0 ] ); - } - - return match; - }, - - PSEUDO: function( match ) { - var excess, - unquoted = !match[ 6 ] && match[ 2 ]; - - if ( matchExpr.CHILD.test( match[ 0 ] ) ) { - return null; - } - - // Accept quoted arguments as-is - if ( match[ 3 ] ) { - match[ 2 ] = match[ 4 ] || match[ 5 ] || ""; - - // Strip excess characters from unquoted arguments - } else if ( unquoted && rpseudo.test( unquoted ) && - - // Get excess from tokenize (recursively) - ( excess = tokenize( unquoted, true ) ) && - - // advance to the next closing parenthesis - ( excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length ) ) { - - // excess is a negative index - match[ 0 ] = match[ 0 ].slice( 0, excess ); - match[ 2 ] = unquoted.slice( 0, excess ); - } - - // Return only captures needed by the pseudo filter method (type and argument) - return match.slice( 0, 3 ); - } - }, - - filter: { - - TAG: function( nodeNameSelector ) { - var expectedNodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); - return nodeNameSelector === "*" ? - function() { - return true; - } : - function( elem ) { - return nodeName( elem, expectedNodeName ); - }; - }, - - CLASS: function( className ) { - var pattern = classCache[ className + " " ]; - - return pattern || - ( pattern = new RegExp( "(^|" + whitespace + ")" + className + - "(" + whitespace + "|$)" ) ) && - classCache( className, function( elem ) { - return pattern.test( - typeof elem.className === "string" && elem.className || - typeof elem.getAttribute !== "undefined" && - elem.getAttribute( "class" ) || - "" - ); - } ); - }, - - ATTR: function( name, operator, check ) { - return function( elem ) { - var result = find.attr( elem, name ); - - if ( result == null ) { - return operator === "!="; - } - if ( !operator ) { - return true; - } - - result += ""; - - if ( operator === "=" ) { - return result === check; - } - if ( operator === "!=" ) { - return result !== check; - } - if ( operator === "^=" ) { - return check && result.indexOf( check ) === 0; - } - if ( operator === "*=" ) { - return check && result.indexOf( check ) > -1; - } - if ( operator === "$=" ) { - return check && result.slice( -check.length ) === check; - } - if ( operator === "~=" ) { - return ( " " + result.replace( rwhitespace, " " ) + " " ) - .indexOf( check ) > -1; - } - if ( operator === "|=" ) { - return result === check || result.slice( 0, check.length + 1 ) === check + "-"; - } - - return false; - }; - }, - - CHILD: function( type, what, _argument, first, last ) { - var simple = type.slice( 0, 3 ) !== "nth", - forward = type.slice( -4 ) !== "last", - ofType = what === "of-type"; - - return first === 1 && last === 0 ? - - // Shortcut for :nth-*(n) - function( elem ) { - return !!elem.parentNode; - } : - - function( elem, _context, xml ) { - var cache, outerCache, node, nodeIndex, start, - dir = simple !== forward ? "nextSibling" : "previousSibling", - parent = elem.parentNode, - name = ofType && elem.nodeName.toLowerCase(), - useCache = !xml && !ofType, - diff = false; - - if ( parent ) { - - // :(first|last|only)-(child|of-type) - if ( simple ) { - while ( dir ) { - node = elem; - while ( ( node = node[ dir ] ) ) { - if ( ofType ? - nodeName( node, name ) : - node.nodeType === 1 ) { - - return false; - } - } - - // Reverse direction for :only-* (if we haven't yet done so) - start = dir = type === "only" && !start && "nextSibling"; - } - return true; - } - - start = [ forward ? parent.firstChild : parent.lastChild ]; - - // non-xml :nth-child(...) stores cache data on `parent` - if ( forward && useCache ) { - - // Seek `elem` from a previously-cached index - outerCache = parent[ expando ] || ( parent[ expando ] = {} ); - cache = outerCache[ type ] || []; - nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; - diff = nodeIndex && cache[ 2 ]; - node = nodeIndex && parent.childNodes[ nodeIndex ]; - - while ( ( node = ++nodeIndex && node && node[ dir ] || - - // Fallback to seeking `elem` from the start - ( diff = nodeIndex = 0 ) || start.pop() ) ) { - - // When found, cache indexes on `parent` and break - if ( node.nodeType === 1 && ++diff && node === elem ) { - outerCache[ type ] = [ dirruns, nodeIndex, diff ]; - break; - } - } - - } else { - - // Use previously-cached element index if available - if ( useCache ) { - outerCache = elem[ expando ] || ( elem[ expando ] = {} ); - cache = outerCache[ type ] || []; - nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; - diff = nodeIndex; - } - - // xml :nth-child(...) - // or :nth-last-child(...) or :nth(-last)?-of-type(...) - if ( diff === false ) { - - // Use the same loop as above to seek `elem` from the start - while ( ( node = ++nodeIndex && node && node[ dir ] || - ( diff = nodeIndex = 0 ) || start.pop() ) ) { - - if ( ( ofType ? - nodeName( node, name ) : - node.nodeType === 1 ) && - ++diff ) { - - // Cache the index of each encountered element - if ( useCache ) { - outerCache = node[ expando ] || - ( node[ expando ] = {} ); - outerCache[ type ] = [ dirruns, diff ]; - } - - if ( node === elem ) { - break; - } - } - } - } - } - - // Incorporate the offset, then check against cycle size - diff -= last; - return diff === first || ( diff % first === 0 && diff / first >= 0 ); - } - }; - }, - - PSEUDO: function( pseudo, argument ) { - - // pseudo-class names are case-insensitive - // https://www.w3.org/TR/selectors/#pseudo-classes - // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters - // Remember that setFilters inherits from pseudos - var args, - fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || - find.error( "unsupported pseudo: " + pseudo ); - - // The user may use createPseudo to indicate that - // arguments are needed to create the filter function - // just as jQuery does - if ( fn[ expando ] ) { - return fn( argument ); - } - - // But maintain support for old signatures - if ( fn.length > 1 ) { - args = [ pseudo, pseudo, "", argument ]; - return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? - markFunction( function( seed, matches ) { - var idx, - matched = fn( seed, argument ), - i = matched.length; - while ( i-- ) { - idx = indexOf.call( seed, matched[ i ] ); - seed[ idx ] = !( matches[ idx ] = matched[ i ] ); - } - } ) : - function( elem ) { - return fn( elem, 0, args ); - }; - } - - return fn; - } - }, - - pseudos: { - - // Potentially complex pseudos - not: markFunction( function( selector ) { - - // Trim the selector passed to compile - // to avoid treating leading and trailing - // spaces as combinators - var input = [], - results = [], - matcher = compile( selector.replace( rtrimCSS, "$1" ) ); - - return matcher[ expando ] ? - markFunction( function( seed, matches, _context, xml ) { - var elem, - unmatched = matcher( seed, null, xml, [] ), - i = seed.length; - - // Match elements unmatched by `matcher` - while ( i-- ) { - if ( ( elem = unmatched[ i ] ) ) { - seed[ i ] = !( matches[ i ] = elem ); - } - } - } ) : - function( elem, _context, xml ) { - input[ 0 ] = elem; - matcher( input, null, xml, results ); - - // Don't keep the element - // (see https://github.com/jquery/sizzle/issues/299) - input[ 0 ] = null; - return !results.pop(); - }; - } ), - - has: markFunction( function( selector ) { - return function( elem ) { - return find( selector, elem ).length > 0; - }; - } ), - - contains: markFunction( function( text ) { - text = text.replace( runescape, funescape ); - return function( elem ) { - return ( elem.textContent || jQuery.text( elem ) ).indexOf( text ) > -1; - }; - } ), - - // "Whether an element is represented by a :lang() selector - // is based solely on the element's language value - // being equal to the identifier C, - // or beginning with the identifier C immediately followed by "-". - // The matching of C against the element's language value is performed case-insensitively. - // The identifier C does not have to be a valid language name." - // https://www.w3.org/TR/selectors/#lang-pseudo - lang: markFunction( function( lang ) { - - // lang value must be a valid identifier - if ( !ridentifier.test( lang || "" ) ) { - find.error( "unsupported lang: " + lang ); - } - lang = lang.replace( runescape, funescape ).toLowerCase(); - return function( elem ) { - var elemLang; - do { - if ( ( elemLang = documentIsHTML ? - elem.lang : - elem.getAttribute( "xml:lang" ) || elem.getAttribute( "lang" ) ) ) { - - elemLang = elemLang.toLowerCase(); - return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; - } - } while ( ( elem = elem.parentNode ) && elem.nodeType === 1 ); - return false; - }; - } ), - - // Miscellaneous - target: function( elem ) { - var hash = window.location && window.location.hash; - return hash && hash.slice( 1 ) === elem.id; - }, - - root: function( elem ) { - return elem === documentElement; - }, - - focus: function( elem ) { - return elem === safeActiveElement() && - document.hasFocus() && - !!( elem.type || elem.href || ~elem.tabIndex ); - }, - - // Boolean properties - enabled: createDisabledPseudo( false ), - disabled: createDisabledPseudo( true ), - - checked: function( elem ) { - - // In CSS3, :checked should return both checked and selected elements - // https://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked - return ( nodeName( elem, "input" ) && !!elem.checked ) || - ( nodeName( elem, "option" ) && !!elem.selected ); - }, - - selected: function( elem ) { - - // Support: IE <=11+ - // Accessing the selectedIndex property - // forces the browser to treat the default option as - // selected when in an optgroup. - if ( elem.parentNode ) { - // eslint-disable-next-line no-unused-expressions - elem.parentNode.selectedIndex; - } - - return elem.selected === true; - }, - - // Contents - empty: function( elem ) { - - // https://www.w3.org/TR/selectors/#empty-pseudo - // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), - // but not by others (comment: 8; processing instruction: 7; etc.) - // nodeType < 6 works because attributes (2) do not appear as children - for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { - if ( elem.nodeType < 6 ) { - return false; - } - } - return true; - }, - - parent: function( elem ) { - return !Expr.pseudos.empty( elem ); - }, - - // Element/input types - header: function( elem ) { - return rheader.test( elem.nodeName ); - }, - - input: function( elem ) { - return rinputs.test( elem.nodeName ); - }, - - button: function( elem ) { - return nodeName( elem, "input" ) && elem.type === "button" || - nodeName( elem, "button" ); - }, - - text: function( elem ) { - var attr; - return nodeName( elem, "input" ) && elem.type === "text" && - - // Support: IE <10 only - // New HTML5 attribute values (e.g., "search") appear - // with elem.type === "text" - ( ( attr = elem.getAttribute( "type" ) ) == null || - attr.toLowerCase() === "text" ); - }, - - // Position-in-collection - first: createPositionalPseudo( function() { - return [ 0 ]; - } ), - - last: createPositionalPseudo( function( _matchIndexes, length ) { - return [ length - 1 ]; - } ), - - eq: createPositionalPseudo( function( _matchIndexes, length, argument ) { - return [ argument < 0 ? argument + length : argument ]; - } ), - - even: createPositionalPseudo( function( matchIndexes, length ) { - var i = 0; - for ( ; i < length; i += 2 ) { - matchIndexes.push( i ); - } - return matchIndexes; - } ), - - odd: createPositionalPseudo( function( matchIndexes, length ) { - var i = 1; - for ( ; i < length; i += 2 ) { - matchIndexes.push( i ); - } - return matchIndexes; - } ), - - lt: createPositionalPseudo( function( matchIndexes, length, argument ) { - var i; - - if ( argument < 0 ) { - i = argument + length; - } else if ( argument > length ) { - i = length; - } else { - i = argument; - } - - for ( ; --i >= 0; ) { - matchIndexes.push( i ); - } - return matchIndexes; - } ), - - gt: createPositionalPseudo( function( matchIndexes, length, argument ) { - var i = argument < 0 ? argument + length : argument; - for ( ; ++i < length; ) { - matchIndexes.push( i ); - } - return matchIndexes; - } ) - } -}; - -Expr.pseudos.nth = Expr.pseudos.eq; - -// Add button/input type pseudos -for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { - Expr.pseudos[ i ] = createInputPseudo( i ); -} -for ( i in { submit: true, reset: true } ) { - Expr.pseudos[ i ] = createButtonPseudo( i ); -} - -// Easy API for creating new setFilters -function setFilters() {} -setFilters.prototype = Expr.filters = Expr.pseudos; -Expr.setFilters = new setFilters(); - -function tokenize( selector, parseOnly ) { - var matched, match, tokens, type, - soFar, groups, preFilters, - cached = tokenCache[ selector + " " ]; - - if ( cached ) { - return parseOnly ? 0 : cached.slice( 0 ); - } - - soFar = selector; - groups = []; - preFilters = Expr.preFilter; - - while ( soFar ) { - - // Comma and first run - if ( !matched || ( match = rcomma.exec( soFar ) ) ) { - if ( match ) { - - // Don't consume trailing commas as valid - soFar = soFar.slice( match[ 0 ].length ) || soFar; - } - groups.push( ( tokens = [] ) ); - } - - matched = false; - - // Combinators - if ( ( match = rleadingCombinator.exec( soFar ) ) ) { - matched = match.shift(); - tokens.push( { - value: matched, - - // Cast descendant combinators to space - type: match[ 0 ].replace( rtrimCSS, " " ) - } ); - soFar = soFar.slice( matched.length ); - } - - // Filters - for ( type in Expr.filter ) { - if ( ( match = matchExpr[ type ].exec( soFar ) ) && ( !preFilters[ type ] || - ( match = preFilters[ type ]( match ) ) ) ) { - matched = match.shift(); - tokens.push( { - value: matched, - type: type, - matches: match - } ); - soFar = soFar.slice( matched.length ); - } - } - - if ( !matched ) { - break; - } - } - - // Return the length of the invalid excess - // if we're just parsing - // Otherwise, throw an error or return tokens - if ( parseOnly ) { - return soFar.length; - } - - return soFar ? - find.error( selector ) : - - // Cache the tokens - tokenCache( selector, groups ).slice( 0 ); -} - -function toSelector( tokens ) { - var i = 0, - len = tokens.length, - selector = ""; - for ( ; i < len; i++ ) { - selector += tokens[ i ].value; - } - return selector; -} - -function addCombinator( matcher, combinator, base ) { - var dir = combinator.dir, - skip = combinator.next, - key = skip || dir, - checkNonElements = base && key === "parentNode", - doneName = done++; - - return combinator.first ? - - // Check against closest ancestor/preceding element - function( elem, context, xml ) { - while ( ( elem = elem[ dir ] ) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - return matcher( elem, context, xml ); - } - } - return false; - } : - - // Check against all ancestor/preceding elements - function( elem, context, xml ) { - var oldCache, outerCache, - newCache = [ dirruns, doneName ]; - - // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching - if ( xml ) { - while ( ( elem = elem[ dir ] ) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - if ( matcher( elem, context, xml ) ) { - return true; - } - } - } - } else { - while ( ( elem = elem[ dir ] ) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - outerCache = elem[ expando ] || ( elem[ expando ] = {} ); - - if ( skip && nodeName( elem, skip ) ) { - elem = elem[ dir ] || elem; - } else if ( ( oldCache = outerCache[ key ] ) && - oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { - - // Assign to newCache so results back-propagate to previous elements - return ( newCache[ 2 ] = oldCache[ 2 ] ); - } else { - - // Reuse newcache so results back-propagate to previous elements - outerCache[ key ] = newCache; - - // A match means we're done; a fail means we have to keep checking - if ( ( newCache[ 2 ] = matcher( elem, context, xml ) ) ) { - return true; - } - } - } - } - } - return false; - }; -} - -function elementMatcher( matchers ) { - return matchers.length > 1 ? - function( elem, context, xml ) { - var i = matchers.length; - while ( i-- ) { - if ( !matchers[ i ]( elem, context, xml ) ) { - return false; - } - } - return true; - } : - matchers[ 0 ]; -} - -function multipleContexts( selector, contexts, results ) { - var i = 0, - len = contexts.length; - for ( ; i < len; i++ ) { - find( selector, contexts[ i ], results ); - } - return results; -} - -function condense( unmatched, map, filter, context, xml ) { - var elem, - newUnmatched = [], - i = 0, - len = unmatched.length, - mapped = map != null; - - for ( ; i < len; i++ ) { - if ( ( elem = unmatched[ i ] ) ) { - if ( !filter || filter( elem, context, xml ) ) { - newUnmatched.push( elem ); - if ( mapped ) { - map.push( i ); - } - } - } - } - - return newUnmatched; -} - -function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { - if ( postFilter && !postFilter[ expando ] ) { - postFilter = setMatcher( postFilter ); - } - if ( postFinder && !postFinder[ expando ] ) { - postFinder = setMatcher( postFinder, postSelector ); - } - return markFunction( function( seed, results, context, xml ) { - var temp, i, elem, matcherOut, - preMap = [], - postMap = [], - preexisting = results.length, - - // Get initial elements from seed or context - elems = seed || - multipleContexts( selector || "*", - context.nodeType ? [ context ] : context, [] ), - - // Prefilter to get matcher input, preserving a map for seed-results synchronization - matcherIn = preFilter && ( seed || !selector ) ? - condense( elems, preMap, preFilter, context, xml ) : - elems; - - if ( matcher ) { - - // If we have a postFinder, or filtered seed, or non-seed postFilter - // or preexisting results, - matcherOut = postFinder || ( seed ? preFilter : preexisting || postFilter ) ? - - // ...intermediate processing is necessary - [] : - - // ...otherwise use results directly - results; - - // Find primary matches - matcher( matcherIn, matcherOut, context, xml ); - } else { - matcherOut = matcherIn; - } - - // Apply postFilter - if ( postFilter ) { - temp = condense( matcherOut, postMap ); - postFilter( temp, [], context, xml ); - - // Un-match failing elements by moving them back to matcherIn - i = temp.length; - while ( i-- ) { - if ( ( elem = temp[ i ] ) ) { - matcherOut[ postMap[ i ] ] = !( matcherIn[ postMap[ i ] ] = elem ); - } - } - } - - if ( seed ) { - if ( postFinder || preFilter ) { - if ( postFinder ) { - - // Get the final matcherOut by condensing this intermediate into postFinder contexts - temp = []; - i = matcherOut.length; - while ( i-- ) { - if ( ( elem = matcherOut[ i ] ) ) { - - // Restore matcherIn since elem is not yet a final match - temp.push( ( matcherIn[ i ] = elem ) ); - } - } - postFinder( null, ( matcherOut = [] ), temp, xml ); - } - - // Move matched elements from seed to results to keep them synchronized - i = matcherOut.length; - while ( i-- ) { - if ( ( elem = matcherOut[ i ] ) && - ( temp = postFinder ? indexOf.call( seed, elem ) : preMap[ i ] ) > -1 ) { - - seed[ temp ] = !( results[ temp ] = elem ); - } - } - } - - // Add elements to results, through postFinder if defined - } else { - matcherOut = condense( - matcherOut === results ? - matcherOut.splice( preexisting, matcherOut.length ) : - matcherOut - ); - if ( postFinder ) { - postFinder( null, results, matcherOut, xml ); - } else { - push.apply( results, matcherOut ); - } - } - } ); -} - -function matcherFromTokens( tokens ) { - var checkContext, matcher, j, - len = tokens.length, - leadingRelative = Expr.relative[ tokens[ 0 ].type ], - implicitRelative = leadingRelative || Expr.relative[ " " ], - i = leadingRelative ? 1 : 0, - - // The foundational matcher ensures that elements are reachable from top-level context(s) - matchContext = addCombinator( function( elem ) { - return elem === checkContext; - }, implicitRelative, true ), - matchAnyContext = addCombinator( function( elem ) { - return indexOf.call( checkContext, elem ) > -1; - }, implicitRelative, true ), - matchers = [ function( elem, context, xml ) { - - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when strict-comparing - // two documents; shallow comparisons work. - // eslint-disable-next-line eqeqeq - var ret = ( !leadingRelative && ( xml || context != outermostContext ) ) || ( - ( checkContext = context ).nodeType ? - matchContext( elem, context, xml ) : - matchAnyContext( elem, context, xml ) ); - - // Avoid hanging onto element - // (see https://github.com/jquery/sizzle/issues/299) - checkContext = null; - return ret; - } ]; - - for ( ; i < len; i++ ) { - if ( ( matcher = Expr.relative[ tokens[ i ].type ] ) ) { - matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ]; - } else { - matcher = Expr.filter[ tokens[ i ].type ].apply( null, tokens[ i ].matches ); - - // Return special upon seeing a positional matcher - if ( matcher[ expando ] ) { - - // Find the next relative operator (if any) for proper handling - j = ++i; - for ( ; j < len; j++ ) { - if ( Expr.relative[ tokens[ j ].type ] ) { - break; - } - } - return setMatcher( - i > 1 && elementMatcher( matchers ), - i > 1 && toSelector( - - // If the preceding token was a descendant combinator, insert an implicit any-element `*` - tokens.slice( 0, i - 1 ) - .concat( { value: tokens[ i - 2 ].type === " " ? "*" : "" } ) - ).replace( rtrimCSS, "$1" ), - matcher, - i < j && matcherFromTokens( tokens.slice( i, j ) ), - j < len && matcherFromTokens( ( tokens = tokens.slice( j ) ) ), - j < len && toSelector( tokens ) - ); - } - matchers.push( matcher ); - } - } - - return elementMatcher( matchers ); -} - -function matcherFromGroupMatchers( elementMatchers, setMatchers ) { - var bySet = setMatchers.length > 0, - byElement = elementMatchers.length > 0, - superMatcher = function( seed, context, xml, results, outermost ) { - var elem, j, matcher, - matchedCount = 0, - i = "0", - unmatched = seed && [], - setMatched = [], - contextBackup = outermostContext, - - // We must always have either seed elements or outermost context - elems = seed || byElement && Expr.find.TAG( "*", outermost ), - - // Use integer dirruns iff this is the outermost matcher - dirrunsUnique = ( dirruns += contextBackup == null ? 1 : Math.random() || 0.1 ), - len = elems.length; - - if ( outermost ) { - - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when strict-comparing - // two documents; shallow comparisons work. - // eslint-disable-next-line eqeqeq - outermostContext = context == document || context || outermost; - } - - // Add elements passing elementMatchers directly to results - // Support: iOS <=7 - 9 only - // Tolerate NodeList properties (IE: "length"; Safari: ) matching - // elements by id. (see trac-14142) - for ( ; i !== len && ( elem = elems[ i ] ) != null; i++ ) { - if ( byElement && elem ) { - j = 0; - - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when strict-comparing - // two documents; shallow comparisons work. - // eslint-disable-next-line eqeqeq - if ( !context && elem.ownerDocument != document ) { - setDocument( elem ); - xml = !documentIsHTML; - } - while ( ( matcher = elementMatchers[ j++ ] ) ) { - if ( matcher( elem, context || document, xml ) ) { - push.call( results, elem ); - break; - } - } - if ( outermost ) { - dirruns = dirrunsUnique; - } - } - - // Track unmatched elements for set filters - if ( bySet ) { - - // They will have gone through all possible matchers - if ( ( elem = !matcher && elem ) ) { - matchedCount--; - } - - // Lengthen the array for every element, matched or not - if ( seed ) { - unmatched.push( elem ); - } - } - } - - // `i` is now the count of elements visited above, and adding it to `matchedCount` - // makes the latter nonnegative. - matchedCount += i; - - // Apply set filters to unmatched elements - // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount` - // equals `i`), unless we didn't visit _any_ elements in the above loop because we have - // no element matchers and no seed. - // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that - // case, which will result in a "00" `matchedCount` that differs from `i` but is also - // numerically zero. - if ( bySet && i !== matchedCount ) { - j = 0; - while ( ( matcher = setMatchers[ j++ ] ) ) { - matcher( unmatched, setMatched, context, xml ); - } - - if ( seed ) { - - // Reintegrate element matches to eliminate the need for sorting - if ( matchedCount > 0 ) { - while ( i-- ) { - if ( !( unmatched[ i ] || setMatched[ i ] ) ) { - setMatched[ i ] = pop.call( results ); - } - } - } - - // Discard index placeholder values to get only actual matches - setMatched = condense( setMatched ); - } - - // Add matches to results - push.apply( results, setMatched ); - - // Seedless set matches succeeding multiple successful matchers stipulate sorting - if ( outermost && !seed && setMatched.length > 0 && - ( matchedCount + setMatchers.length ) > 1 ) { - - jQuery.uniqueSort( results ); - } - } - - // Override manipulation of globals by nested matchers - if ( outermost ) { - dirruns = dirrunsUnique; - outermostContext = contextBackup; - } - - return unmatched; - }; - - return bySet ? - markFunction( superMatcher ) : - superMatcher; -} - -function compile( selector, match /* Internal Use Only */ ) { - var i, - setMatchers = [], - elementMatchers = [], - cached = compilerCache[ selector + " " ]; - - if ( !cached ) { - - // Generate a function of recursive functions that can be used to check each element - if ( !match ) { - match = tokenize( selector ); - } - i = match.length; - while ( i-- ) { - cached = matcherFromTokens( match[ i ] ); - if ( cached[ expando ] ) { - setMatchers.push( cached ); - } else { - elementMatchers.push( cached ); - } - } - - // Cache the compiled function - cached = compilerCache( selector, - matcherFromGroupMatchers( elementMatchers, setMatchers ) ); - - // Save selector and tokenization - cached.selector = selector; - } - return cached; -} - -/** - * A low-level selection function that works with jQuery's compiled - * selector functions - * @param {String|Function} selector A selector or a pre-compiled - * selector function built with jQuery selector compile - * @param {Element} context - * @param {Array} [results] - * @param {Array} [seed] A set of elements to match against - */ -function select( selector, context, results, seed ) { - var i, tokens, token, type, find, - compiled = typeof selector === "function" && selector, - match = !seed && tokenize( ( selector = compiled.selector || selector ) ); - - results = results || []; - - // Try to minimize operations if there is only one selector in the list and no seed - // (the latter of which guarantees us context) - if ( match.length === 1 ) { - - // Reduce context if the leading compound selector is an ID - tokens = match[ 0 ] = match[ 0 ].slice( 0 ); - if ( tokens.length > 2 && ( token = tokens[ 0 ] ).type === "ID" && - context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[ 1 ].type ] ) { - - context = ( Expr.find.ID( - token.matches[ 0 ].replace( runescape, funescape ), - context - ) || [] )[ 0 ]; - if ( !context ) { - return results; - - // Precompiled matchers will still verify ancestry, so step up a level - } else if ( compiled ) { - context = context.parentNode; - } - - selector = selector.slice( tokens.shift().value.length ); - } - - // Fetch a seed set for right-to-left matching - i = matchExpr.needsContext.test( selector ) ? 0 : tokens.length; - while ( i-- ) { - token = tokens[ i ]; - - // Abort if we hit a combinator - if ( Expr.relative[ ( type = token.type ) ] ) { - break; - } - if ( ( find = Expr.find[ type ] ) ) { - - // Search, expanding context for leading sibling combinators - if ( ( seed = find( - token.matches[ 0 ].replace( runescape, funescape ), - rsibling.test( tokens[ 0 ].type ) && - testContext( context.parentNode ) || context - ) ) ) { - - // If seed is empty or no tokens remain, we can return early - tokens.splice( i, 1 ); - selector = seed.length && toSelector( tokens ); - if ( !selector ) { - push.apply( results, seed ); - return results; - } - - break; - } - } - } - } - - // Compile and execute a filtering function if one is not provided - // Provide `match` to avoid retokenization if we modified the selector above - ( compiled || compile( selector, match ) )( - seed, - context, - !documentIsHTML, - results, - !context || rsibling.test( selector ) && testContext( context.parentNode ) || context - ); - return results; -} - -// One-time assignments - -// Support: Android <=4.0 - 4.1+ -// Sort stability -support.sortStable = expando.split( "" ).sort( sortOrder ).join( "" ) === expando; - -// Initialize against the default document -setDocument(); - -// Support: Android <=4.0 - 4.1+ -// Detached nodes confoundingly follow *each other* -support.sortDetached = assert( function( el ) { - - // Should return 1, but returns 4 (following) - return el.compareDocumentPosition( document.createElement( "fieldset" ) ) & 1; -} ); - -jQuery.find = find; - -// Deprecated -jQuery.expr[ ":" ] = jQuery.expr.pseudos; -jQuery.unique = jQuery.uniqueSort; - -// These have always been private, but they used to be documented as part of -// Sizzle so let's maintain them for now for backwards compatibility purposes. -find.compile = compile; -find.select = select; -find.setDocument = setDocument; -find.tokenize = tokenize; - -find.escape = jQuery.escapeSelector; -find.getText = jQuery.text; -find.isXML = jQuery.isXMLDoc; -find.selectors = jQuery.expr; -find.support = jQuery.support; -find.uniqueSort = jQuery.uniqueSort; - - /* eslint-enable */ - -} )(); - - -var dir = function( elem, dir, until ) { - var matched = [], - truncate = until !== undefined; - - while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) { - if ( elem.nodeType === 1 ) { - if ( truncate && jQuery( elem ).is( until ) ) { - break; - } - matched.push( elem ); - } - } - return matched; -}; - - -var siblings = function( n, elem ) { - var matched = []; - - for ( ; n; n = n.nextSibling ) { - if ( n.nodeType === 1 && n !== elem ) { - matched.push( n ); - } - } - - return matched; -}; - - -var rneedsContext = jQuery.expr.match.needsContext; - -var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i ); - - - -// Implement the identical functionality for filter and not -function winnow( elements, qualifier, not ) { - if ( isFunction( qualifier ) ) { - return jQuery.grep( elements, function( elem, i ) { - return !!qualifier.call( elem, i, elem ) !== not; - } ); - } - - // Single element - if ( qualifier.nodeType ) { - return jQuery.grep( elements, function( elem ) { - return ( elem === qualifier ) !== not; - } ); - } - - // Arraylike of elements (jQuery, arguments, Array) - if ( typeof qualifier !== "string" ) { - return jQuery.grep( elements, function( elem ) { - return ( indexOf.call( qualifier, elem ) > -1 ) !== not; - } ); - } - - // Filtered directly for both simple and complex selectors - return jQuery.filter( qualifier, elements, not ); -} - -jQuery.filter = function( expr, elems, not ) { - var elem = elems[ 0 ]; - - if ( not ) { - expr = ":not(" + expr + ")"; - } - - if ( elems.length === 1 && elem.nodeType === 1 ) { - return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : []; - } - - return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { - return elem.nodeType === 1; - } ) ); -}; - -jQuery.fn.extend( { - find: function( selector ) { - var i, ret, - len = this.length, - self = this; - - if ( typeof selector !== "string" ) { - return this.pushStack( jQuery( selector ).filter( function() { - for ( i = 0; i < len; i++ ) { - if ( jQuery.contains( self[ i ], this ) ) { - return true; - } - } - } ) ); - } - - ret = this.pushStack( [] ); - - for ( i = 0; i < len; i++ ) { - jQuery.find( selector, self[ i ], ret ); - } - - return len > 1 ? jQuery.uniqueSort( ret ) : ret; - }, - filter: function( selector ) { - return this.pushStack( winnow( this, selector || [], false ) ); - }, - not: function( selector ) { - return this.pushStack( winnow( this, selector || [], true ) ); - }, - is: function( selector ) { - return !!winnow( - this, - - // If this is a positional/relative selector, check membership in the returned set - // so $("p:first").is("p:last") won't return true for a doc with two "p". - typeof selector === "string" && rneedsContext.test( selector ) ? - jQuery( selector ) : - selector || [], - false - ).length; - } -} ); - - -// Initialize a jQuery object - - -// A central reference to the root jQuery(document) -var rootjQuery, - - // A simple way to check for HTML strings - // Prioritize #id over to avoid XSS via location.hash (trac-9521) - // Strict HTML recognition (trac-11290: must start with <) - // Shortcut simple #id case for speed - rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/, - - init = jQuery.fn.init = function( selector, context, root ) { - var match, elem; - - // HANDLE: $(""), $(null), $(undefined), $(false) - if ( !selector ) { - return this; - } - - // Method init() accepts an alternate rootjQuery - // so migrate can support jQuery.sub (gh-2101) - root = root || rootjQuery; - - // Handle HTML strings - if ( typeof selector === "string" ) { - if ( selector[ 0 ] === "<" && - selector[ selector.length - 1 ] === ">" && - selector.length >= 3 ) { - - // Assume that strings that start and end with <> are HTML and skip the regex check - match = [ null, selector, null ]; - - } else { - match = rquickExpr.exec( selector ); - } - - // Match html or make sure no context is specified for #id - if ( match && ( match[ 1 ] || !context ) ) { - - // HANDLE: $(html) -> $(array) - if ( match[ 1 ] ) { - context = context instanceof jQuery ? context[ 0 ] : context; - - // Option to run scripts is true for back-compat - // Intentionally let the error be thrown if parseHTML is not present - jQuery.merge( this, jQuery.parseHTML( - match[ 1 ], - context && context.nodeType ? context.ownerDocument || context : document, - true - ) ); - - // HANDLE: $(html, props) - if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { - for ( match in context ) { - - // Properties of context are called as methods if possible - if ( isFunction( this[ match ] ) ) { - this[ match ]( context[ match ] ); - - // ...and otherwise set as attributes - } else { - this.attr( match, context[ match ] ); - } - } - } - - return this; - - // HANDLE: $(#id) - } else { - elem = document.getElementById( match[ 2 ] ); - - if ( elem ) { - - // Inject the element directly into the jQuery object - this[ 0 ] = elem; - this.length = 1; - } - return this; - } - - // HANDLE: $(expr, $(...)) - } else if ( !context || context.jquery ) { - return ( context || root ).find( selector ); - - // HANDLE: $(expr, context) - // (which is just equivalent to: $(context).find(expr) - } else { - return this.constructor( context ).find( selector ); - } - - // HANDLE: $(DOMElement) - } else if ( selector.nodeType ) { - this[ 0 ] = selector; - this.length = 1; - return this; - - // HANDLE: $(function) - // Shortcut for document ready - } else if ( isFunction( selector ) ) { - return root.ready !== undefined ? - root.ready( selector ) : - - // Execute immediately if ready is not present - selector( jQuery ); - } - - return jQuery.makeArray( selector, this ); - }; - -// Give the init function the jQuery prototype for later instantiation -init.prototype = jQuery.fn; - -// Initialize central reference -rootjQuery = jQuery( document ); - - -var rparentsprev = /^(?:parents|prev(?:Until|All))/, - - // Methods guaranteed to produce a unique set when starting from a unique set - guaranteedUnique = { - children: true, - contents: true, - next: true, - prev: true - }; - -jQuery.fn.extend( { - has: function( target ) { - var targets = jQuery( target, this ), - l = targets.length; - - return this.filter( function() { - var i = 0; - for ( ; i < l; i++ ) { - if ( jQuery.contains( this, targets[ i ] ) ) { - return true; - } - } - } ); - }, - - closest: function( selectors, context ) { - var cur, - i = 0, - l = this.length, - matched = [], - targets = typeof selectors !== "string" && jQuery( selectors ); - - // Positional selectors never match, since there's no _selection_ context - if ( !rneedsContext.test( selectors ) ) { - for ( ; i < l; i++ ) { - for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) { - - // Always skip document fragments - if ( cur.nodeType < 11 && ( targets ? - targets.index( cur ) > -1 : - - // Don't pass non-elements to jQuery#find - cur.nodeType === 1 && - jQuery.find.matchesSelector( cur, selectors ) ) ) { - - matched.push( cur ); - break; - } - } - } - } - - return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched ); - }, - - // Determine the position of an element within the set - index: function( elem ) { - - // No argument, return index in parent - if ( !elem ) { - return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; - } - - // Index in selector - if ( typeof elem === "string" ) { - return indexOf.call( jQuery( elem ), this[ 0 ] ); - } - - // Locate the position of the desired element - return indexOf.call( this, - - // If it receives a jQuery object, the first element is used - elem.jquery ? elem[ 0 ] : elem - ); - }, - - add: function( selector, context ) { - return this.pushStack( - jQuery.uniqueSort( - jQuery.merge( this.get(), jQuery( selector, context ) ) - ) - ); - }, - - addBack: function( selector ) { - return this.add( selector == null ? - this.prevObject : this.prevObject.filter( selector ) - ); - } -} ); - -function sibling( cur, dir ) { - while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {} - return cur; -} - -jQuery.each( { - parent: function( elem ) { - var parent = elem.parentNode; - return parent && parent.nodeType !== 11 ? parent : null; - }, - parents: function( elem ) { - return dir( elem, "parentNode" ); - }, - parentsUntil: function( elem, _i, until ) { - return dir( elem, "parentNode", until ); - }, - next: function( elem ) { - return sibling( elem, "nextSibling" ); - }, - prev: function( elem ) { - return sibling( elem, "previousSibling" ); - }, - nextAll: function( elem ) { - return dir( elem, "nextSibling" ); - }, - prevAll: function( elem ) { - return dir( elem, "previousSibling" ); - }, - nextUntil: function( elem, _i, until ) { - return dir( elem, "nextSibling", until ); - }, - prevUntil: function( elem, _i, until ) { - return dir( elem, "previousSibling", until ); - }, - siblings: function( elem ) { - return siblings( ( elem.parentNode || {} ).firstChild, elem ); - }, - children: function( elem ) { - return siblings( elem.firstChild ); - }, - contents: function( elem ) { - if ( elem.contentDocument != null && - - // Support: IE 11+ - // elements with no `data` attribute has an object - // `contentDocument` with a `null` prototype. - getProto( elem.contentDocument ) ) { - - return elem.contentDocument; - } - - // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only - // Treat the template element as a regular one in browsers that - // don't support it. - if ( nodeName( elem, "template" ) ) { - elem = elem.content || elem; - } - - return jQuery.merge( [], elem.childNodes ); - } -}, function( name, fn ) { - jQuery.fn[ name ] = function( until, selector ) { - var matched = jQuery.map( this, fn, until ); - - if ( name.slice( -5 ) !== "Until" ) { - selector = until; - } - - if ( selector && typeof selector === "string" ) { - matched = jQuery.filter( selector, matched ); - } - - if ( this.length > 1 ) { - - // Remove duplicates - if ( !guaranteedUnique[ name ] ) { - jQuery.uniqueSort( matched ); - } - - // Reverse order for parents* and prev-derivatives - if ( rparentsprev.test( name ) ) { - matched.reverse(); - } - } - - return this.pushStack( matched ); - }; -} ); -var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g ); - - - -// Convert String-formatted options into Object-formatted ones -function createOptions( options ) { - var object = {}; - jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) { - object[ flag ] = true; - } ); - return object; -} - -/* - * Create a callback list using the following parameters: - * - * options: an optional list of space-separated options that will change how - * the callback list behaves or a more traditional option object - * - * By default a callback list will act like an event callback list and can be - * "fired" multiple times. - * - * Possible options: - * - * once: will ensure the callback list can only be fired once (like a Deferred) - * - * memory: will keep track of previous values and will call any callback added - * after the list has been fired right away with the latest "memorized" - * values (like a Deferred) - * - * unique: will ensure a callback can only be added once (no duplicate in the list) - * - * stopOnFalse: interrupt callings when a callback returns false - * - */ -jQuery.Callbacks = function( options ) { - - // Convert options from String-formatted to Object-formatted if needed - // (we check in cache first) - options = typeof options === "string" ? - createOptions( options ) : - jQuery.extend( {}, options ); - - var // Flag to know if list is currently firing - firing, - - // Last fire value for non-forgettable lists - memory, - - // Flag to know if list was already fired - fired, - - // Flag to prevent firing - locked, - - // Actual callback list - list = [], - - // Queue of execution data for repeatable lists - queue = [], - - // Index of currently firing callback (modified by add/remove as needed) - firingIndex = -1, - - // Fire callbacks - fire = function() { - - // Enforce single-firing - locked = locked || options.once; - - // Execute callbacks for all pending executions, - // respecting firingIndex overrides and runtime changes - fired = firing = true; - for ( ; queue.length; firingIndex = -1 ) { - memory = queue.shift(); - while ( ++firingIndex < list.length ) { - - // Run callback and check for early termination - if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false && - options.stopOnFalse ) { - - // Jump to end and forget the data so .add doesn't re-fire - firingIndex = list.length; - memory = false; - } - } - } - - // Forget the data if we're done with it - if ( !options.memory ) { - memory = false; - } - - firing = false; - - // Clean up if we're done firing for good - if ( locked ) { - - // Keep an empty list if we have data for future add calls - if ( memory ) { - list = []; - - // Otherwise, this object is spent - } else { - list = ""; - } - } - }, - - // Actual Callbacks object - self = { - - // Add a callback or a collection of callbacks to the list - add: function() { - if ( list ) { - - // If we have memory from a past run, we should fire after adding - if ( memory && !firing ) { - firingIndex = list.length - 1; - queue.push( memory ); - } - - ( function add( args ) { - jQuery.each( args, function( _, arg ) { - if ( isFunction( arg ) ) { - if ( !options.unique || !self.has( arg ) ) { - list.push( arg ); - } - } else if ( arg && arg.length && toType( arg ) !== "string" ) { - - // Inspect recursively - add( arg ); - } - } ); - } )( arguments ); - - if ( memory && !firing ) { - fire(); - } - } - return this; - }, - - // Remove a callback from the list - remove: function() { - jQuery.each( arguments, function( _, arg ) { - var index; - while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { - list.splice( index, 1 ); - - // Handle firing indexes - if ( index <= firingIndex ) { - firingIndex--; - } - } - } ); - return this; - }, - - // Check if a given callback is in the list. - // If no argument is given, return whether or not list has callbacks attached. - has: function( fn ) { - return fn ? - jQuery.inArray( fn, list ) > -1 : - list.length > 0; - }, - - // Remove all callbacks from the list - empty: function() { - if ( list ) { - list = []; - } - return this; - }, - - // Disable .fire and .add - // Abort any current/pending executions - // Clear all callbacks and values - disable: function() { - locked = queue = []; - list = memory = ""; - return this; - }, - disabled: function() { - return !list; - }, - - // Disable .fire - // Also disable .add unless we have memory (since it would have no effect) - // Abort any pending executions - lock: function() { - locked = queue = []; - if ( !memory && !firing ) { - list = memory = ""; - } - return this; - }, - locked: function() { - return !!locked; - }, - - // Call all callbacks with the given context and arguments - fireWith: function( context, args ) { - if ( !locked ) { - args = args || []; - args = [ context, args.slice ? args.slice() : args ]; - queue.push( args ); - if ( !firing ) { - fire(); - } - } - return this; - }, - - // Call all the callbacks with the given arguments - fire: function() { - self.fireWith( this, arguments ); - return this; - }, - - // To know if the callbacks have already been called at least once - fired: function() { - return !!fired; - } - }; - - return self; -}; - - -function Identity( v ) { - return v; -} -function Thrower( ex ) { - throw ex; -} - -function adoptValue( value, resolve, reject, noValue ) { - var method; - - try { - - // Check for promise aspect first to privilege synchronous behavior - if ( value && isFunction( ( method = value.promise ) ) ) { - method.call( value ).done( resolve ).fail( reject ); - - // Other thenables - } else if ( value && isFunction( ( method = value.then ) ) ) { - method.call( value, resolve, reject ); - - // Other non-thenables - } else { - - // Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer: - // * false: [ value ].slice( 0 ) => resolve( value ) - // * true: [ value ].slice( 1 ) => resolve() - resolve.apply( undefined, [ value ].slice( noValue ) ); - } - - // For Promises/A+, convert exceptions into rejections - // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in - // Deferred#then to conditionally suppress rejection. - } catch ( value ) { - - // Support: Android 4.0 only - // Strict mode functions invoked without .call/.apply get global-object context - reject.apply( undefined, [ value ] ); - } -} - -jQuery.extend( { - - Deferred: function( func ) { - var tuples = [ - - // action, add listener, callbacks, - // ... .then handlers, argument index, [final state] - [ "notify", "progress", jQuery.Callbacks( "memory" ), - jQuery.Callbacks( "memory" ), 2 ], - [ "resolve", "done", jQuery.Callbacks( "once memory" ), - jQuery.Callbacks( "once memory" ), 0, "resolved" ], - [ "reject", "fail", jQuery.Callbacks( "once memory" ), - jQuery.Callbacks( "once memory" ), 1, "rejected" ] - ], - state = "pending", - promise = { - state: function() { - return state; - }, - always: function() { - deferred.done( arguments ).fail( arguments ); - return this; - }, - "catch": function( fn ) { - return promise.then( null, fn ); - }, - - // Keep pipe for back-compat - pipe: function( /* fnDone, fnFail, fnProgress */ ) { - var fns = arguments; - - return jQuery.Deferred( function( newDefer ) { - jQuery.each( tuples, function( _i, tuple ) { - - // Map tuples (progress, done, fail) to arguments (done, fail, progress) - var fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ]; - - // deferred.progress(function() { bind to newDefer or newDefer.notify }) - // deferred.done(function() { bind to newDefer or newDefer.resolve }) - // deferred.fail(function() { bind to newDefer or newDefer.reject }) - deferred[ tuple[ 1 ] ]( function() { - var returned = fn && fn.apply( this, arguments ); - if ( returned && isFunction( returned.promise ) ) { - returned.promise() - .progress( newDefer.notify ) - .done( newDefer.resolve ) - .fail( newDefer.reject ); - } else { - newDefer[ tuple[ 0 ] + "With" ]( - this, - fn ? [ returned ] : arguments - ); - } - } ); - } ); - fns = null; - } ).promise(); - }, - then: function( onFulfilled, onRejected, onProgress ) { - var maxDepth = 0; - function resolve( depth, deferred, handler, special ) { - return function() { - var that = this, - args = arguments, - mightThrow = function() { - var returned, then; - - // Support: Promises/A+ section 2.3.3.3.3 - // https://promisesaplus.com/#point-59 - // Ignore double-resolution attempts - if ( depth < maxDepth ) { - return; - } - - returned = handler.apply( that, args ); - - // Support: Promises/A+ section 2.3.1 - // https://promisesaplus.com/#point-48 - if ( returned === deferred.promise() ) { - throw new TypeError( "Thenable self-resolution" ); - } - - // Support: Promises/A+ sections 2.3.3.1, 3.5 - // https://promisesaplus.com/#point-54 - // https://promisesaplus.com/#point-75 - // Retrieve `then` only once - then = returned && - - // Support: Promises/A+ section 2.3.4 - // https://promisesaplus.com/#point-64 - // Only check objects and functions for thenability - ( typeof returned === "object" || - typeof returned === "function" ) && - returned.then; - - // Handle a returned thenable - if ( isFunction( then ) ) { - - // Special processors (notify) just wait for resolution - if ( special ) { - then.call( - returned, - resolve( maxDepth, deferred, Identity, special ), - resolve( maxDepth, deferred, Thrower, special ) - ); - - // Normal processors (resolve) also hook into progress - } else { - - // ...and disregard older resolution values - maxDepth++; - - then.call( - returned, - resolve( maxDepth, deferred, Identity, special ), - resolve( maxDepth, deferred, Thrower, special ), - resolve( maxDepth, deferred, Identity, - deferred.notifyWith ) - ); - } - - // Handle all other returned values - } else { - - // Only substitute handlers pass on context - // and multiple values (non-spec behavior) - if ( handler !== Identity ) { - that = undefined; - args = [ returned ]; - } - - // Process the value(s) - // Default process is resolve - ( special || deferred.resolveWith )( that, args ); - } - }, - - // Only normal processors (resolve) catch and reject exceptions - process = special ? - mightThrow : - function() { - try { - mightThrow(); - } catch ( e ) { - - if ( jQuery.Deferred.exceptionHook ) { - jQuery.Deferred.exceptionHook( e, - process.error ); - } - - // Support: Promises/A+ section 2.3.3.3.4.1 - // https://promisesaplus.com/#point-61 - // Ignore post-resolution exceptions - if ( depth + 1 >= maxDepth ) { - - // Only substitute handlers pass on context - // and multiple values (non-spec behavior) - if ( handler !== Thrower ) { - that = undefined; - args = [ e ]; - } - - deferred.rejectWith( that, args ); - } - } - }; - - // Support: Promises/A+ section 2.3.3.3.1 - // https://promisesaplus.com/#point-57 - // Re-resolve promises immediately to dodge false rejection from - // subsequent errors - if ( depth ) { - process(); - } else { - - // Call an optional hook to record the error, in case of exception - // since it's otherwise lost when execution goes async - if ( jQuery.Deferred.getErrorHook ) { - process.error = jQuery.Deferred.getErrorHook(); - - // The deprecated alias of the above. While the name suggests - // returning the stack, not an error instance, jQuery just passes - // it directly to `console.warn` so both will work; an instance - // just better cooperates with source maps. - } else if ( jQuery.Deferred.getStackHook ) { - process.error = jQuery.Deferred.getStackHook(); - } - window.setTimeout( process ); - } - }; - } - - return jQuery.Deferred( function( newDefer ) { - - // progress_handlers.add( ... ) - tuples[ 0 ][ 3 ].add( - resolve( - 0, - newDefer, - isFunction( onProgress ) ? - onProgress : - Identity, - newDefer.notifyWith - ) - ); - - // fulfilled_handlers.add( ... ) - tuples[ 1 ][ 3 ].add( - resolve( - 0, - newDefer, - isFunction( onFulfilled ) ? - onFulfilled : - Identity - ) - ); - - // rejected_handlers.add( ... ) - tuples[ 2 ][ 3 ].add( - resolve( - 0, - newDefer, - isFunction( onRejected ) ? - onRejected : - Thrower - ) - ); - } ).promise(); - }, - - // Get a promise for this deferred - // If obj is provided, the promise aspect is added to the object - promise: function( obj ) { - return obj != null ? jQuery.extend( obj, promise ) : promise; - } - }, - deferred = {}; - - // Add list-specific methods - jQuery.each( tuples, function( i, tuple ) { - var list = tuple[ 2 ], - stateString = tuple[ 5 ]; - - // promise.progress = list.add - // promise.done = list.add - // promise.fail = list.add - promise[ tuple[ 1 ] ] = list.add; - - // Handle state - if ( stateString ) { - list.add( - function() { - - // state = "resolved" (i.e., fulfilled) - // state = "rejected" - state = stateString; - }, - - // rejected_callbacks.disable - // fulfilled_callbacks.disable - tuples[ 3 - i ][ 2 ].disable, - - // rejected_handlers.disable - // fulfilled_handlers.disable - tuples[ 3 - i ][ 3 ].disable, - - // progress_callbacks.lock - tuples[ 0 ][ 2 ].lock, - - // progress_handlers.lock - tuples[ 0 ][ 3 ].lock - ); - } - - // progress_handlers.fire - // fulfilled_handlers.fire - // rejected_handlers.fire - list.add( tuple[ 3 ].fire ); - - // deferred.notify = function() { deferred.notifyWith(...) } - // deferred.resolve = function() { deferred.resolveWith(...) } - // deferred.reject = function() { deferred.rejectWith(...) } - deferred[ tuple[ 0 ] ] = function() { - deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments ); - return this; - }; - - // deferred.notifyWith = list.fireWith - // deferred.resolveWith = list.fireWith - // deferred.rejectWith = list.fireWith - deferred[ tuple[ 0 ] + "With" ] = list.fireWith; - } ); - - // Make the deferred a promise - promise.promise( deferred ); - - // Call given func if any - if ( func ) { - func.call( deferred, deferred ); - } - - // All done! - return deferred; - }, - - // Deferred helper - when: function( singleValue ) { - var - - // count of uncompleted subordinates - remaining = arguments.length, - - // count of unprocessed arguments - i = remaining, - - // subordinate fulfillment data - resolveContexts = Array( i ), - resolveValues = slice.call( arguments ), - - // the primary Deferred - primary = jQuery.Deferred(), - - // subordinate callback factory - updateFunc = function( i ) { - return function( value ) { - resolveContexts[ i ] = this; - resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; - if ( !( --remaining ) ) { - primary.resolveWith( resolveContexts, resolveValues ); - } - }; - }; - - // Single- and empty arguments are adopted like Promise.resolve - if ( remaining <= 1 ) { - adoptValue( singleValue, primary.done( updateFunc( i ) ).resolve, primary.reject, - !remaining ); - - // Use .then() to unwrap secondary thenables (cf. gh-3000) - if ( primary.state() === "pending" || - isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) { - - return primary.then(); - } - } - - // Multiple arguments are aggregated like Promise.all array elements - while ( i-- ) { - adoptValue( resolveValues[ i ], updateFunc( i ), primary.reject ); - } - - return primary.promise(); - } -} ); - - -// These usually indicate a programmer mistake during development, -// warn about them ASAP rather than swallowing them by default. -var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/; - -// If `jQuery.Deferred.getErrorHook` is defined, `asyncError` is an error -// captured before the async barrier to get the original error cause -// which may otherwise be hidden. -jQuery.Deferred.exceptionHook = function( error, asyncError ) { - - // Support: IE 8 - 9 only - // Console exists when dev tools are open, which can happen at any time - if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) { - window.console.warn( "jQuery.Deferred exception: " + error.message, - error.stack, asyncError ); - } -}; - - - - -jQuery.readyException = function( error ) { - window.setTimeout( function() { - throw error; - } ); -}; - - - - -// The deferred used on DOM ready -var readyList = jQuery.Deferred(); - -jQuery.fn.ready = function( fn ) { - - readyList - .then( fn ) - - // Wrap jQuery.readyException in a function so that the lookup - // happens at the time of error handling instead of callback - // registration. - .catch( function( error ) { - jQuery.readyException( error ); - } ); - - return this; -}; - -jQuery.extend( { - - // Is the DOM ready to be used? Set to true once it occurs. - isReady: false, - - // A counter to track how many items to wait for before - // the ready event fires. See trac-6781 - readyWait: 1, - - // Handle when the DOM is ready - ready: function( wait ) { - - // Abort if there are pending holds or we're already ready - if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { - return; - } - - // Remember that the DOM is ready - jQuery.isReady = true; - - // If a normal DOM Ready event fired, decrement, and wait if need be - if ( wait !== true && --jQuery.readyWait > 0 ) { - return; - } - - // If there are functions bound, to execute - readyList.resolveWith( document, [ jQuery ] ); - } -} ); - -jQuery.ready.then = readyList.then; - -// The ready event handler and self cleanup method -function completed() { - document.removeEventListener( "DOMContentLoaded", completed ); - window.removeEventListener( "load", completed ); - jQuery.ready(); -} - -// Catch cases where $(document).ready() is called -// after the browser event has already occurred. -// Support: IE <=9 - 10 only -// Older IE sometimes signals "interactive" too soon -if ( document.readyState === "complete" || - ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) { - - // Handle it asynchronously to allow scripts the opportunity to delay ready - window.setTimeout( jQuery.ready ); - -} else { - - // Use the handy event callback - document.addEventListener( "DOMContentLoaded", completed ); - - // A fallback to window.onload, that will always work - window.addEventListener( "load", completed ); -} - - - - -// Multifunctional method to get and set values of a collection -// The value/s can optionally be executed if it's a function -var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { - var i = 0, - len = elems.length, - bulk = key == null; - - // Sets many values - if ( toType( key ) === "object" ) { - chainable = true; - for ( i in key ) { - access( elems, fn, i, key[ i ], true, emptyGet, raw ); - } - - // Sets one value - } else if ( value !== undefined ) { - chainable = true; - - if ( !isFunction( value ) ) { - raw = true; - } - - if ( bulk ) { - - // Bulk operations run against the entire set - if ( raw ) { - fn.call( elems, value ); - fn = null; - - // ...except when executing function values - } else { - bulk = fn; - fn = function( elem, _key, value ) { - return bulk.call( jQuery( elem ), value ); - }; - } - } - - if ( fn ) { - for ( ; i < len; i++ ) { - fn( - elems[ i ], key, raw ? - value : - value.call( elems[ i ], i, fn( elems[ i ], key ) ) - ); - } - } - } - - if ( chainable ) { - return elems; - } - - // Gets - if ( bulk ) { - return fn.call( elems ); - } - - return len ? fn( elems[ 0 ], key ) : emptyGet; -}; - - -// Matches dashed string for camelizing -var rmsPrefix = /^-ms-/, - rdashAlpha = /-([a-z])/g; - -// Used by camelCase as callback to replace() -function fcamelCase( _all, letter ) { - return letter.toUpperCase(); -} - -// Convert dashed to camelCase; used by the css and data modules -// Support: IE <=9 - 11, Edge 12 - 15 -// Microsoft forgot to hump their vendor prefix (trac-9572) -function camelCase( string ) { - return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); -} -var acceptData = function( owner ) { - - // Accepts only: - // - Node - // - Node.ELEMENT_NODE - // - Node.DOCUMENT_NODE - // - Object - // - Any - return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); -}; - - - - -function Data() { - this.expando = jQuery.expando + Data.uid++; -} - -Data.uid = 1; - -Data.prototype = { - - cache: function( owner ) { - - // Check if the owner object already has a cache - var value = owner[ this.expando ]; - - // If not, create one - if ( !value ) { - value = {}; - - // We can accept data for non-element nodes in modern browsers, - // but we should not, see trac-8335. - // Always return an empty object. - if ( acceptData( owner ) ) { - - // If it is a node unlikely to be stringify-ed or looped over - // use plain assignment - if ( owner.nodeType ) { - owner[ this.expando ] = value; - - // Otherwise secure it in a non-enumerable property - // configurable must be true to allow the property to be - // deleted when data is removed - } else { - Object.defineProperty( owner, this.expando, { - value: value, - configurable: true - } ); - } - } - } - - return value; - }, - set: function( owner, data, value ) { - var prop, - cache = this.cache( owner ); - - // Handle: [ owner, key, value ] args - // Always use camelCase key (gh-2257) - if ( typeof data === "string" ) { - cache[ camelCase( data ) ] = value; - - // Handle: [ owner, { properties } ] args - } else { - - // Copy the properties one-by-one to the cache object - for ( prop in data ) { - cache[ camelCase( prop ) ] = data[ prop ]; - } - } - return cache; - }, - get: function( owner, key ) { - return key === undefined ? - this.cache( owner ) : - - // Always use camelCase key (gh-2257) - owner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ]; - }, - access: function( owner, key, value ) { - - // In cases where either: - // - // 1. No key was specified - // 2. A string key was specified, but no value provided - // - // Take the "read" path and allow the get method to determine - // which value to return, respectively either: - // - // 1. The entire cache object - // 2. The data stored at the key - // - if ( key === undefined || - ( ( key && typeof key === "string" ) && value === undefined ) ) { - - return this.get( owner, key ); - } - - // When the key is not a string, or both a key and value - // are specified, set or extend (existing objects) with either: - // - // 1. An object of properties - // 2. A key and value - // - this.set( owner, key, value ); - - // Since the "set" path can have two possible entry points - // return the expected data based on which path was taken[*] - return value !== undefined ? value : key; - }, - remove: function( owner, key ) { - var i, - cache = owner[ this.expando ]; - - if ( cache === undefined ) { - return; - } - - if ( key !== undefined ) { - - // Support array or space separated string of keys - if ( Array.isArray( key ) ) { - - // If key is an array of keys... - // We always set camelCase keys, so remove that. - key = key.map( camelCase ); - } else { - key = camelCase( key ); - - // If a key with the spaces exists, use it. - // Otherwise, create an array by matching non-whitespace - key = key in cache ? - [ key ] : - ( key.match( rnothtmlwhite ) || [] ); - } - - i = key.length; - - while ( i-- ) { - delete cache[ key[ i ] ]; - } - } - - // Remove the expando if there's no more data - if ( key === undefined || jQuery.isEmptyObject( cache ) ) { - - // Support: Chrome <=35 - 45 - // Webkit & Blink performance suffers when deleting properties - // from DOM nodes, so set to undefined instead - // https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted) - if ( owner.nodeType ) { - owner[ this.expando ] = undefined; - } else { - delete owner[ this.expando ]; - } - } - }, - hasData: function( owner ) { - var cache = owner[ this.expando ]; - return cache !== undefined && !jQuery.isEmptyObject( cache ); - } -}; -var dataPriv = new Data(); - -var dataUser = new Data(); - - - -// Implementation Summary -// -// 1. Enforce API surface and semantic compatibility with 1.9.x branch -// 2. Improve the module's maintainability by reducing the storage -// paths to a single mechanism. -// 3. Use the same single mechanism to support "private" and "user" data. -// 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) -// 5. Avoid exposing implementation details on user objects (eg. expando properties) -// 6. Provide a clear path for implementation upgrade to WeakMap in 2014 - -var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, - rmultiDash = /[A-Z]/g; - -function getData( data ) { - if ( data === "true" ) { - return true; - } - - if ( data === "false" ) { - return false; - } - - if ( data === "null" ) { - return null; - } - - // Only convert to a number if it doesn't change the string - if ( data === +data + "" ) { - return +data; - } - - if ( rbrace.test( data ) ) { - return JSON.parse( data ); - } - - return data; -} - -function dataAttr( elem, key, data ) { - var name; - - // If nothing was found internally, try to fetch any - // data from the HTML5 data-* attribute - if ( data === undefined && elem.nodeType === 1 ) { - name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase(); - data = elem.getAttribute( name ); - - if ( typeof data === "string" ) { - try { - data = getData( data ); - } catch ( e ) {} - - // Make sure we set the data so it isn't changed later - dataUser.set( elem, key, data ); - } else { - data = undefined; - } - } - return data; -} - -jQuery.extend( { - hasData: function( elem ) { - return dataUser.hasData( elem ) || dataPriv.hasData( elem ); - }, - - data: function( elem, name, data ) { - return dataUser.access( elem, name, data ); - }, - - removeData: function( elem, name ) { - dataUser.remove( elem, name ); - }, - - // TODO: Now that all calls to _data and _removeData have been replaced - // with direct calls to dataPriv methods, these can be deprecated. - _data: function( elem, name, data ) { - return dataPriv.access( elem, name, data ); - }, - - _removeData: function( elem, name ) { - dataPriv.remove( elem, name ); - } -} ); - -jQuery.fn.extend( { - data: function( key, value ) { - var i, name, data, - elem = this[ 0 ], - attrs = elem && elem.attributes; - - // Gets all values - if ( key === undefined ) { - if ( this.length ) { - data = dataUser.get( elem ); - - if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) { - i = attrs.length; - while ( i-- ) { - - // Support: IE 11 only - // The attrs elements can be null (trac-14894) - if ( attrs[ i ] ) { - name = attrs[ i ].name; - if ( name.indexOf( "data-" ) === 0 ) { - name = camelCase( name.slice( 5 ) ); - dataAttr( elem, name, data[ name ] ); - } - } - } - dataPriv.set( elem, "hasDataAttrs", true ); - } - } - - return data; - } - - // Sets multiple values - if ( typeof key === "object" ) { - return this.each( function() { - dataUser.set( this, key ); - } ); - } - - return access( this, function( value ) { - var data; - - // The calling jQuery object (element matches) is not empty - // (and therefore has an element appears at this[ 0 ]) and the - // `value` parameter was not undefined. An empty jQuery object - // will result in `undefined` for elem = this[ 0 ] which will - // throw an exception if an attempt to read a data cache is made. - if ( elem && value === undefined ) { - - // Attempt to get data from the cache - // The key will always be camelCased in Data - data = dataUser.get( elem, key ); - if ( data !== undefined ) { - return data; - } - - // Attempt to "discover" the data in - // HTML5 custom data-* attrs - data = dataAttr( elem, key ); - if ( data !== undefined ) { - return data; - } - - // We tried really hard, but the data doesn't exist. - return; - } - - // Set the data... - this.each( function() { - - // We always store the camelCased key - dataUser.set( this, key, value ); - } ); - }, null, value, arguments.length > 1, null, true ); - }, - - removeData: function( key ) { - return this.each( function() { - dataUser.remove( this, key ); - } ); - } -} ); - - -jQuery.extend( { - queue: function( elem, type, data ) { - var queue; - - if ( elem ) { - type = ( type || "fx" ) + "queue"; - queue = dataPriv.get( elem, type ); - - // Speed up dequeue by getting out quickly if this is just a lookup - if ( data ) { - if ( !queue || Array.isArray( data ) ) { - queue = dataPriv.access( elem, type, jQuery.makeArray( data ) ); - } else { - queue.push( data ); - } - } - return queue || []; - } - }, - - dequeue: function( elem, type ) { - type = type || "fx"; - - var queue = jQuery.queue( elem, type ), - startLength = queue.length, - fn = queue.shift(), - hooks = jQuery._queueHooks( elem, type ), - next = function() { - jQuery.dequeue( elem, type ); - }; - - // If the fx queue is dequeued, always remove the progress sentinel - if ( fn === "inprogress" ) { - fn = queue.shift(); - startLength--; - } - - if ( fn ) { - - // Add a progress sentinel to prevent the fx queue from being - // automatically dequeued - if ( type === "fx" ) { - queue.unshift( "inprogress" ); - } - - // Clear up the last queue stop function - delete hooks.stop; - fn.call( elem, next, hooks ); - } - - if ( !startLength && hooks ) { - hooks.empty.fire(); - } - }, - - // Not public - generate a queueHooks object, or return the current one - _queueHooks: function( elem, type ) { - var key = type + "queueHooks"; - return dataPriv.get( elem, key ) || dataPriv.access( elem, key, { - empty: jQuery.Callbacks( "once memory" ).add( function() { - dataPriv.remove( elem, [ type + "queue", key ] ); - } ) - } ); - } -} ); - -jQuery.fn.extend( { - queue: function( type, data ) { - var setter = 2; - - if ( typeof type !== "string" ) { - data = type; - type = "fx"; - setter--; - } - - if ( arguments.length < setter ) { - return jQuery.queue( this[ 0 ], type ); - } - - return data === undefined ? - this : - this.each( function() { - var queue = jQuery.queue( this, type, data ); - - // Ensure a hooks for this queue - jQuery._queueHooks( this, type ); - - if ( type === "fx" && queue[ 0 ] !== "inprogress" ) { - jQuery.dequeue( this, type ); - } - } ); - }, - dequeue: function( type ) { - return this.each( function() { - jQuery.dequeue( this, type ); - } ); - }, - clearQueue: function( type ) { - return this.queue( type || "fx", [] ); - }, - - // Get a promise resolved when queues of a certain type - // are emptied (fx is the type by default) - promise: function( type, obj ) { - var tmp, - count = 1, - defer = jQuery.Deferred(), - elements = this, - i = this.length, - resolve = function() { - if ( !( --count ) ) { - defer.resolveWith( elements, [ elements ] ); - } - }; - - if ( typeof type !== "string" ) { - obj = type; - type = undefined; - } - type = type || "fx"; - - while ( i-- ) { - tmp = dataPriv.get( elements[ i ], type + "queueHooks" ); - if ( tmp && tmp.empty ) { - count++; - tmp.empty.add( resolve ); - } - } - resolve(); - return defer.promise( obj ); - } -} ); -var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source; - -var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ); - - -var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; - -var documentElement = document.documentElement; - - - - var isAttached = function( elem ) { - return jQuery.contains( elem.ownerDocument, elem ); - }, - composed = { composed: true }; - - // Support: IE 9 - 11+, Edge 12 - 18+, iOS 10.0 - 10.2 only - // Check attachment across shadow DOM boundaries when possible (gh-3504) - // Support: iOS 10.0-10.2 only - // Early iOS 10 versions support `attachShadow` but not `getRootNode`, - // leading to errors. We need to check for `getRootNode`. - if ( documentElement.getRootNode ) { - isAttached = function( elem ) { - return jQuery.contains( elem.ownerDocument, elem ) || - elem.getRootNode( composed ) === elem.ownerDocument; - }; - } -var isHiddenWithinTree = function( elem, el ) { - - // isHiddenWithinTree might be called from jQuery#filter function; - // in that case, element will be second argument - elem = el || elem; - - // Inline style trumps all - return elem.style.display === "none" || - elem.style.display === "" && - - // Otherwise, check computed style - // Support: Firefox <=43 - 45 - // Disconnected elements can have computed display: none, so first confirm that elem is - // in the document. - isAttached( elem ) && - - jQuery.css( elem, "display" ) === "none"; - }; - - - -function adjustCSS( elem, prop, valueParts, tween ) { - var adjusted, scale, - maxIterations = 20, - currentValue = tween ? - function() { - return tween.cur(); - } : - function() { - return jQuery.css( elem, prop, "" ); - }, - initial = currentValue(), - unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), - - // Starting value computation is required for potential unit mismatches - initialInUnit = elem.nodeType && - ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) && - rcssNum.exec( jQuery.css( elem, prop ) ); - - if ( initialInUnit && initialInUnit[ 3 ] !== unit ) { - - // Support: Firefox <=54 - // Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144) - initial = initial / 2; - - // Trust units reported by jQuery.css - unit = unit || initialInUnit[ 3 ]; - - // Iteratively approximate from a nonzero starting point - initialInUnit = +initial || 1; - - while ( maxIterations-- ) { - - // Evaluate and update our best guess (doubling guesses that zero out). - // Finish if the scale equals or crosses 1 (making the old*new product non-positive). - jQuery.style( elem, prop, initialInUnit + unit ); - if ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) { - maxIterations = 0; - } - initialInUnit = initialInUnit / scale; - - } - - initialInUnit = initialInUnit * 2; - jQuery.style( elem, prop, initialInUnit + unit ); - - // Make sure we update the tween properties later on - valueParts = valueParts || []; - } - - if ( valueParts ) { - initialInUnit = +initialInUnit || +initial || 0; - - // Apply relative offset (+=/-=) if specified - adjusted = valueParts[ 1 ] ? - initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] : - +valueParts[ 2 ]; - if ( tween ) { - tween.unit = unit; - tween.start = initialInUnit; - tween.end = adjusted; - } - } - return adjusted; -} - - -var defaultDisplayMap = {}; - -function getDefaultDisplay( elem ) { - var temp, - doc = elem.ownerDocument, - nodeName = elem.nodeName, - display = defaultDisplayMap[ nodeName ]; - - if ( display ) { - return display; - } - - temp = doc.body.appendChild( doc.createElement( nodeName ) ); - display = jQuery.css( temp, "display" ); - - temp.parentNode.removeChild( temp ); - - if ( display === "none" ) { - display = "block"; - } - defaultDisplayMap[ nodeName ] = display; - - return display; -} - -function showHide( elements, show ) { - var display, elem, - values = [], - index = 0, - length = elements.length; - - // Determine new display value for elements that need to change - for ( ; index < length; index++ ) { - elem = elements[ index ]; - if ( !elem.style ) { - continue; - } - - display = elem.style.display; - if ( show ) { - - // Since we force visibility upon cascade-hidden elements, an immediate (and slow) - // check is required in this first loop unless we have a nonempty display value (either - // inline or about-to-be-restored) - if ( display === "none" ) { - values[ index ] = dataPriv.get( elem, "display" ) || null; - if ( !values[ index ] ) { - elem.style.display = ""; - } - } - if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) { - values[ index ] = getDefaultDisplay( elem ); - } - } else { - if ( display !== "none" ) { - values[ index ] = "none"; - - // Remember what we're overwriting - dataPriv.set( elem, "display", display ); - } - } - } - - // Set the display of the elements in a second loop to avoid constant reflow - for ( index = 0; index < length; index++ ) { - if ( values[ index ] != null ) { - elements[ index ].style.display = values[ index ]; - } - } - - return elements; -} - -jQuery.fn.extend( { - show: function() { - return showHide( this, true ); - }, - hide: function() { - return showHide( this ); - }, - toggle: function( state ) { - if ( typeof state === "boolean" ) { - return state ? this.show() : this.hide(); - } - - return this.each( function() { - if ( isHiddenWithinTree( this ) ) { - jQuery( this ).show(); - } else { - jQuery( this ).hide(); - } - } ); - } -} ); -var rcheckableType = ( /^(?:checkbox|radio)$/i ); - -var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]*)/i ); - -var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i ); - - - -( function() { - var fragment = document.createDocumentFragment(), - div = fragment.appendChild( document.createElement( "div" ) ), - input = document.createElement( "input" ); - - // Support: Android 4.0 - 4.3 only - // Check state lost if the name is set (trac-11217) - // Support: Windows Web Apps (WWA) - // `name` and `type` must use .setAttribute for WWA (trac-14901) - input.setAttribute( "type", "radio" ); - input.setAttribute( "checked", "checked" ); - input.setAttribute( "name", "t" ); - - div.appendChild( input ); - - // Support: Android <=4.1 only - // Older WebKit doesn't clone checked state correctly in fragments - support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; - - // Support: IE <=11 only - // Make sure textarea (and checkbox) defaultValue is properly cloned - div.innerHTML = ""; - support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; - - // Support: IE <=9 only - // IE <=9 replaces "; - support.option = !!div.lastChild; -} )(); - - -// We have to close these tags to support XHTML (trac-13200) -var wrapMap = { - - // XHTML parsers do not magically insert elements in the - // same way that tag soup parsers do. So we cannot shorten - // this by omitting or other required elements. - thead: [ 1, "", "
    " ], - col: [ 2, "", "
    " ], - tr: [ 2, "", "
    " ], - td: [ 3, "", "
    " ], - - _default: [ 0, "", "" ] -}; - -wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; -wrapMap.th = wrapMap.td; - -// Support: IE <=9 only -if ( !support.option ) { - wrapMap.optgroup = wrapMap.option = [ 1, "" ]; -} - - -function getAll( context, tag ) { - - // Support: IE <=9 - 11 only - // Use typeof to avoid zero-argument method invocation on host objects (trac-15151) - var ret; - - if ( typeof context.getElementsByTagName !== "undefined" ) { - ret = context.getElementsByTagName( tag || "*" ); - - } else if ( typeof context.querySelectorAll !== "undefined" ) { - ret = context.querySelectorAll( tag || "*" ); - - } else { - ret = []; - } - - if ( tag === undefined || tag && nodeName( context, tag ) ) { - return jQuery.merge( [ context ], ret ); - } - - return ret; -} - - -// Mark scripts as having already been evaluated -function setGlobalEval( elems, refElements ) { - var i = 0, - l = elems.length; - - for ( ; i < l; i++ ) { - dataPriv.set( - elems[ i ], - "globalEval", - !refElements || dataPriv.get( refElements[ i ], "globalEval" ) - ); - } -} - - -var rhtml = /<|&#?\w+;/; - -function buildFragment( elems, context, scripts, selection, ignored ) { - var elem, tmp, tag, wrap, attached, j, - fragment = context.createDocumentFragment(), - nodes = [], - i = 0, - l = elems.length; - - for ( ; i < l; i++ ) { - elem = elems[ i ]; - - if ( elem || elem === 0 ) { - - // Add nodes directly - if ( toType( elem ) === "object" ) { - - // Support: Android <=4.0 only, PhantomJS 1 only - // push.apply(_, arraylike) throws on ancient WebKit - jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); - - // Convert non-html into a text node - } else if ( !rhtml.test( elem ) ) { - nodes.push( context.createTextNode( elem ) ); - - // Convert html into DOM nodes - } else { - tmp = tmp || fragment.appendChild( context.createElement( "div" ) ); - - // Deserialize a standard representation - tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); - wrap = wrapMap[ tag ] || wrapMap._default; - tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ]; - - // Descend through wrappers to the right content - j = wrap[ 0 ]; - while ( j-- ) { - tmp = tmp.lastChild; - } - - // Support: Android <=4.0 only, PhantomJS 1 only - // push.apply(_, arraylike) throws on ancient WebKit - jQuery.merge( nodes, tmp.childNodes ); - - // Remember the top-level container - tmp = fragment.firstChild; - - // Ensure the created nodes are orphaned (trac-12392) - tmp.textContent = ""; - } - } - } - - // Remove wrapper from fragment - fragment.textContent = ""; - - i = 0; - while ( ( elem = nodes[ i++ ] ) ) { - - // Skip elements already in the context collection (trac-4087) - if ( selection && jQuery.inArray( elem, selection ) > -1 ) { - if ( ignored ) { - ignored.push( elem ); - } - continue; - } - - attached = isAttached( elem ); - - // Append to fragment - tmp = getAll( fragment.appendChild( elem ), "script" ); - - // Preserve script evaluation history - if ( attached ) { - setGlobalEval( tmp ); - } - - // Capture executables - if ( scripts ) { - j = 0; - while ( ( elem = tmp[ j++ ] ) ) { - if ( rscriptType.test( elem.type || "" ) ) { - scripts.push( elem ); - } - } - } - } - - return fragment; -} - - -var rtypenamespace = /^([^.]*)(?:\.(.+)|)/; - -function returnTrue() { - return true; -} - -function returnFalse() { - return false; -} - -function on( elem, types, selector, data, fn, one ) { - var origFn, type; - - // Types can be a map of types/handlers - if ( typeof types === "object" ) { - - // ( types-Object, selector, data ) - if ( typeof selector !== "string" ) { - - // ( types-Object, data ) - data = data || selector; - selector = undefined; - } - for ( type in types ) { - on( elem, type, selector, data, types[ type ], one ); - } - return elem; - } - - if ( data == null && fn == null ) { - - // ( types, fn ) - fn = selector; - data = selector = undefined; - } else if ( fn == null ) { - if ( typeof selector === "string" ) { - - // ( types, selector, fn ) - fn = data; - data = undefined; - } else { - - // ( types, data, fn ) - fn = data; - data = selector; - selector = undefined; - } - } - if ( fn === false ) { - fn = returnFalse; - } else if ( !fn ) { - return elem; - } - - if ( one === 1 ) { - origFn = fn; - fn = function( event ) { - - // Can use an empty set, since event contains the info - jQuery().off( event ); - return origFn.apply( this, arguments ); - }; - - // Use same guid so caller can remove using origFn - fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); - } - return elem.each( function() { - jQuery.event.add( this, types, fn, data, selector ); - } ); -} - -/* - * Helper functions for managing events -- not part of the public interface. - * Props to Dean Edwards' addEvent library for many of the ideas. - */ -jQuery.event = { - - global: {}, - - add: function( elem, types, handler, data, selector ) { - - var handleObjIn, eventHandle, tmp, - events, t, handleObj, - special, handlers, type, namespaces, origType, - elemData = dataPriv.get( elem ); - - // Only attach events to objects that accept data - if ( !acceptData( elem ) ) { - return; - } - - // Caller can pass in an object of custom data in lieu of the handler - if ( handler.handler ) { - handleObjIn = handler; - handler = handleObjIn.handler; - selector = handleObjIn.selector; - } - - // Ensure that invalid selectors throw exceptions at attach time - // Evaluate against documentElement in case elem is a non-element node (e.g., document) - if ( selector ) { - jQuery.find.matchesSelector( documentElement, selector ); - } - - // Make sure that the handler has a unique ID, used to find/remove it later - if ( !handler.guid ) { - handler.guid = jQuery.guid++; - } - - // Init the element's event structure and main handler, if this is the first - if ( !( events = elemData.events ) ) { - events = elemData.events = Object.create( null ); - } - if ( !( eventHandle = elemData.handle ) ) { - eventHandle = elemData.handle = function( e ) { - - // Discard the second event of a jQuery.event.trigger() and - // when an event is called after a page has unloaded - return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? - jQuery.event.dispatch.apply( elem, arguments ) : undefined; - }; - } - - // Handle multiple events separated by a space - types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; - t = types.length; - while ( t-- ) { - tmp = rtypenamespace.exec( types[ t ] ) || []; - type = origType = tmp[ 1 ]; - namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); - - // There *must* be a type, no attaching namespace-only handlers - if ( !type ) { - continue; - } - - // If event changes its type, use the special event handlers for the changed type - special = jQuery.event.special[ type ] || {}; - - // If selector defined, determine special event api type, otherwise given type - type = ( selector ? special.delegateType : special.bindType ) || type; - - // Update special based on newly reset type - special = jQuery.event.special[ type ] || {}; - - // handleObj is passed to all event handlers - handleObj = jQuery.extend( { - type: type, - origType: origType, - data: data, - handler: handler, - guid: handler.guid, - selector: selector, - needsContext: selector && jQuery.expr.match.needsContext.test( selector ), - namespace: namespaces.join( "." ) - }, handleObjIn ); - - // Init the event handler queue if we're the first - if ( !( handlers = events[ type ] ) ) { - handlers = events[ type ] = []; - handlers.delegateCount = 0; - - // Only use addEventListener if the special events handler returns false - if ( !special.setup || - special.setup.call( elem, data, namespaces, eventHandle ) === false ) { - - if ( elem.addEventListener ) { - elem.addEventListener( type, eventHandle ); - } - } - } - - if ( special.add ) { - special.add.call( elem, handleObj ); - - if ( !handleObj.handler.guid ) { - handleObj.handler.guid = handler.guid; - } - } - - // Add to the element's handler list, delegates in front - if ( selector ) { - handlers.splice( handlers.delegateCount++, 0, handleObj ); - } else { - handlers.push( handleObj ); - } - - // Keep track of which events have ever been used, for event optimization - jQuery.event.global[ type ] = true; - } - - }, - - // Detach an event or set of events from an element - remove: function( elem, types, handler, selector, mappedTypes ) { - - var j, origCount, tmp, - events, t, handleObj, - special, handlers, type, namespaces, origType, - elemData = dataPriv.hasData( elem ) && dataPriv.get( elem ); - - if ( !elemData || !( events = elemData.events ) ) { - return; - } - - // Once for each type.namespace in types; type may be omitted - types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; - t = types.length; - while ( t-- ) { - tmp = rtypenamespace.exec( types[ t ] ) || []; - type = origType = tmp[ 1 ]; - namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); - - // Unbind all events (on this namespace, if provided) for the element - if ( !type ) { - for ( type in events ) { - jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); - } - continue; - } - - special = jQuery.event.special[ type ] || {}; - type = ( selector ? special.delegateType : special.bindType ) || type; - handlers = events[ type ] || []; - tmp = tmp[ 2 ] && - new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ); - - // Remove matching events - origCount = j = handlers.length; - while ( j-- ) { - handleObj = handlers[ j ]; - - if ( ( mappedTypes || origType === handleObj.origType ) && - ( !handler || handler.guid === handleObj.guid ) && - ( !tmp || tmp.test( handleObj.namespace ) ) && - ( !selector || selector === handleObj.selector || - selector === "**" && handleObj.selector ) ) { - handlers.splice( j, 1 ); - - if ( handleObj.selector ) { - handlers.delegateCount--; - } - if ( special.remove ) { - special.remove.call( elem, handleObj ); - } - } - } - - // Remove generic event handler if we removed something and no more handlers exist - // (avoids potential for endless recursion during removal of special event handlers) - if ( origCount && !handlers.length ) { - if ( !special.teardown || - special.teardown.call( elem, namespaces, elemData.handle ) === false ) { - - jQuery.removeEvent( elem, type, elemData.handle ); - } - - delete events[ type ]; - } - } - - // Remove data and the expando if it's no longer used - if ( jQuery.isEmptyObject( events ) ) { - dataPriv.remove( elem, "handle events" ); - } - }, - - dispatch: function( nativeEvent ) { - - var i, j, ret, matched, handleObj, handlerQueue, - args = new Array( arguments.length ), - - // Make a writable jQuery.Event from the native event object - event = jQuery.event.fix( nativeEvent ), - - handlers = ( - dataPriv.get( this, "events" ) || Object.create( null ) - )[ event.type ] || [], - special = jQuery.event.special[ event.type ] || {}; - - // Use the fix-ed jQuery.Event rather than the (read-only) native event - args[ 0 ] = event; - - for ( i = 1; i < arguments.length; i++ ) { - args[ i ] = arguments[ i ]; - } - - event.delegateTarget = this; - - // Call the preDispatch hook for the mapped type, and let it bail if desired - if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { - return; - } - - // Determine handlers - handlerQueue = jQuery.event.handlers.call( this, event, handlers ); - - // Run delegates first; they may want to stop propagation beneath us - i = 0; - while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { - event.currentTarget = matched.elem; - - j = 0; - while ( ( handleObj = matched.handlers[ j++ ] ) && - !event.isImmediatePropagationStopped() ) { - - // If the event is namespaced, then each handler is only invoked if it is - // specially universal or its namespaces are a superset of the event's. - if ( !event.rnamespace || handleObj.namespace === false || - event.rnamespace.test( handleObj.namespace ) ) { - - event.handleObj = handleObj; - event.data = handleObj.data; - - ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || - handleObj.handler ).apply( matched.elem, args ); - - if ( ret !== undefined ) { - if ( ( event.result = ret ) === false ) { - event.preventDefault(); - event.stopPropagation(); - } - } - } - } - } - - // Call the postDispatch hook for the mapped type - if ( special.postDispatch ) { - special.postDispatch.call( this, event ); - } - - return event.result; - }, - - handlers: function( event, handlers ) { - var i, handleObj, sel, matchedHandlers, matchedSelectors, - handlerQueue = [], - delegateCount = handlers.delegateCount, - cur = event.target; - - // Find delegate handlers - if ( delegateCount && - - // Support: IE <=9 - // Black-hole SVG instance trees (trac-13180) - cur.nodeType && - - // Support: Firefox <=42 - // Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861) - // https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click - // Support: IE 11 only - // ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343) - !( event.type === "click" && event.button >= 1 ) ) { - - for ( ; cur !== this; cur = cur.parentNode || this ) { - - // Don't check non-elements (trac-13208) - // Don't process clicks on disabled elements (trac-6911, trac-8165, trac-11382, trac-11764) - if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) { - matchedHandlers = []; - matchedSelectors = {}; - for ( i = 0; i < delegateCount; i++ ) { - handleObj = handlers[ i ]; - - // Don't conflict with Object.prototype properties (trac-13203) - sel = handleObj.selector + " "; - - if ( matchedSelectors[ sel ] === undefined ) { - matchedSelectors[ sel ] = handleObj.needsContext ? - jQuery( sel, this ).index( cur ) > -1 : - jQuery.find( sel, this, null, [ cur ] ).length; - } - if ( matchedSelectors[ sel ] ) { - matchedHandlers.push( handleObj ); - } - } - if ( matchedHandlers.length ) { - handlerQueue.push( { elem: cur, handlers: matchedHandlers } ); - } - } - } - } - - // Add the remaining (directly-bound) handlers - cur = this; - if ( delegateCount < handlers.length ) { - handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } ); - } - - return handlerQueue; - }, - - addProp: function( name, hook ) { - Object.defineProperty( jQuery.Event.prototype, name, { - enumerable: true, - configurable: true, - - get: isFunction( hook ) ? - function() { - if ( this.originalEvent ) { - return hook( this.originalEvent ); - } - } : - function() { - if ( this.originalEvent ) { - return this.originalEvent[ name ]; - } - }, - - set: function( value ) { - Object.defineProperty( this, name, { - enumerable: true, - configurable: true, - writable: true, - value: value - } ); - } - } ); - }, - - fix: function( originalEvent ) { - return originalEvent[ jQuery.expando ] ? - originalEvent : - new jQuery.Event( originalEvent ); - }, - - special: { - load: { - - // Prevent triggered image.load events from bubbling to window.load - noBubble: true - }, - click: { - - // Utilize native event to ensure correct state for checkable inputs - setup: function( data ) { - - // For mutual compressibility with _default, replace `this` access with a local var. - // `|| data` is dead code meant only to preserve the variable through minification. - var el = this || data; - - // Claim the first handler - if ( rcheckableType.test( el.type ) && - el.click && nodeName( el, "input" ) ) { - - // dataPriv.set( el, "click", ... ) - leverageNative( el, "click", true ); - } - - // Return false to allow normal processing in the caller - return false; - }, - trigger: function( data ) { - - // For mutual compressibility with _default, replace `this` access with a local var. - // `|| data` is dead code meant only to preserve the variable through minification. - var el = this || data; - - // Force setup before triggering a click - if ( rcheckableType.test( el.type ) && - el.click && nodeName( el, "input" ) ) { - - leverageNative( el, "click" ); - } - - // Return non-false to allow normal event-path propagation - return true; - }, - - // For cross-browser consistency, suppress native .click() on links - // Also prevent it if we're currently inside a leveraged native-event stack - _default: function( event ) { - var target = event.target; - return rcheckableType.test( target.type ) && - target.click && nodeName( target, "input" ) && - dataPriv.get( target, "click" ) || - nodeName( target, "a" ); - } - }, - - beforeunload: { - postDispatch: function( event ) { - - // Support: Firefox 20+ - // Firefox doesn't alert if the returnValue field is not set. - if ( event.result !== undefined && event.originalEvent ) { - event.originalEvent.returnValue = event.result; - } - } - } - } -}; - -// Ensure the presence of an event listener that handles manually-triggered -// synthetic events by interrupting progress until reinvoked in response to -// *native* events that it fires directly, ensuring that state changes have -// already occurred before other listeners are invoked. -function leverageNative( el, type, isSetup ) { - - // Missing `isSetup` indicates a trigger call, which must force setup through jQuery.event.add - if ( !isSetup ) { - if ( dataPriv.get( el, type ) === undefined ) { - jQuery.event.add( el, type, returnTrue ); - } - return; - } - - // Register the controller as a special universal handler for all event namespaces - dataPriv.set( el, type, false ); - jQuery.event.add( el, type, { - namespace: false, - handler: function( event ) { - var result, - saved = dataPriv.get( this, type ); - - if ( ( event.isTrigger & 1 ) && this[ type ] ) { - - // Interrupt processing of the outer synthetic .trigger()ed event - if ( !saved ) { - - // Store arguments for use when handling the inner native event - // There will always be at least one argument (an event object), so this array - // will not be confused with a leftover capture object. - saved = slice.call( arguments ); - dataPriv.set( this, type, saved ); - - // Trigger the native event and capture its result - this[ type ](); - result = dataPriv.get( this, type ); - dataPriv.set( this, type, false ); - - if ( saved !== result ) { - - // Cancel the outer synthetic event - event.stopImmediatePropagation(); - event.preventDefault(); - - return result; - } - - // If this is an inner synthetic event for an event with a bubbling surrogate - // (focus or blur), assume that the surrogate already propagated from triggering - // the native event and prevent that from happening again here. - // This technically gets the ordering wrong w.r.t. to `.trigger()` (in which the - // bubbling surrogate propagates *after* the non-bubbling base), but that seems - // less bad than duplication. - } else if ( ( jQuery.event.special[ type ] || {} ).delegateType ) { - event.stopPropagation(); - } - - // If this is a native event triggered above, everything is now in order - // Fire an inner synthetic event with the original arguments - } else if ( saved ) { - - // ...and capture the result - dataPriv.set( this, type, jQuery.event.trigger( - saved[ 0 ], - saved.slice( 1 ), - this - ) ); - - // Abort handling of the native event by all jQuery handlers while allowing - // native handlers on the same element to run. On target, this is achieved - // by stopping immediate propagation just on the jQuery event. However, - // the native event is re-wrapped by a jQuery one on each level of the - // propagation so the only way to stop it for jQuery is to stop it for - // everyone via native `stopPropagation()`. This is not a problem for - // focus/blur which don't bubble, but it does also stop click on checkboxes - // and radios. We accept this limitation. - event.stopPropagation(); - event.isImmediatePropagationStopped = returnTrue; - } - } - } ); -} - -jQuery.removeEvent = function( elem, type, handle ) { - - // This "if" is needed for plain objects - if ( elem.removeEventListener ) { - elem.removeEventListener( type, handle ); - } -}; - -jQuery.Event = function( src, props ) { - - // Allow instantiation without the 'new' keyword - if ( !( this instanceof jQuery.Event ) ) { - return new jQuery.Event( src, props ); - } - - // Event object - if ( src && src.type ) { - this.originalEvent = src; - this.type = src.type; - - // Events bubbling up the document may have been marked as prevented - // by a handler lower down the tree; reflect the correct value. - this.isDefaultPrevented = src.defaultPrevented || - src.defaultPrevented === undefined && - - // Support: Android <=2.3 only - src.returnValue === false ? - returnTrue : - returnFalse; - - // Create target properties - // Support: Safari <=6 - 7 only - // Target should not be a text node (trac-504, trac-13143) - this.target = ( src.target && src.target.nodeType === 3 ) ? - src.target.parentNode : - src.target; - - this.currentTarget = src.currentTarget; - this.relatedTarget = src.relatedTarget; - - // Event type - } else { - this.type = src; - } - - // Put explicitly provided properties onto the event object - if ( props ) { - jQuery.extend( this, props ); - } - - // Create a timestamp if incoming event doesn't have one - this.timeStamp = src && src.timeStamp || Date.now(); - - // Mark it as fixed - this[ jQuery.expando ] = true; -}; - -// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding -// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html -jQuery.Event.prototype = { - constructor: jQuery.Event, - isDefaultPrevented: returnFalse, - isPropagationStopped: returnFalse, - isImmediatePropagationStopped: returnFalse, - isSimulated: false, - - preventDefault: function() { - var e = this.originalEvent; - - this.isDefaultPrevented = returnTrue; - - if ( e && !this.isSimulated ) { - e.preventDefault(); - } - }, - stopPropagation: function() { - var e = this.originalEvent; - - this.isPropagationStopped = returnTrue; - - if ( e && !this.isSimulated ) { - e.stopPropagation(); - } - }, - stopImmediatePropagation: function() { - var e = this.originalEvent; - - this.isImmediatePropagationStopped = returnTrue; - - if ( e && !this.isSimulated ) { - e.stopImmediatePropagation(); - } - - this.stopPropagation(); - } -}; - -// Includes all common event props including KeyEvent and MouseEvent specific props -jQuery.each( { - altKey: true, - bubbles: true, - cancelable: true, - changedTouches: true, - ctrlKey: true, - detail: true, - eventPhase: true, - metaKey: true, - pageX: true, - pageY: true, - shiftKey: true, - view: true, - "char": true, - code: true, - charCode: true, - key: true, - keyCode: true, - button: true, - buttons: true, - clientX: true, - clientY: true, - offsetX: true, - offsetY: true, - pointerId: true, - pointerType: true, - screenX: true, - screenY: true, - targetTouches: true, - toElement: true, - touches: true, - which: true -}, jQuery.event.addProp ); - -jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateType ) { - - function focusMappedHandler( nativeEvent ) { - if ( document.documentMode ) { - - // Support: IE 11+ - // Attach a single focusin/focusout handler on the document while someone wants - // focus/blur. This is because the former are synchronous in IE while the latter - // are async. In other browsers, all those handlers are invoked synchronously. - - // `handle` from private data would already wrap the event, but we need - // to change the `type` here. - var handle = dataPriv.get( this, "handle" ), - event = jQuery.event.fix( nativeEvent ); - event.type = nativeEvent.type === "focusin" ? "focus" : "blur"; - event.isSimulated = true; - - // First, handle focusin/focusout - handle( nativeEvent ); - - // ...then, handle focus/blur - // - // focus/blur don't bubble while focusin/focusout do; simulate the former by only - // invoking the handler at the lower level. - if ( event.target === event.currentTarget ) { - - // The setup part calls `leverageNative`, which, in turn, calls - // `jQuery.event.add`, so event handle will already have been set - // by this point. - handle( event ); - } - } else { - - // For non-IE browsers, attach a single capturing handler on the document - // while someone wants focusin/focusout. - jQuery.event.simulate( delegateType, nativeEvent.target, - jQuery.event.fix( nativeEvent ) ); - } - } - - jQuery.event.special[ type ] = { - - // Utilize native event if possible so blur/focus sequence is correct - setup: function() { - - var attaches; - - // Claim the first handler - // dataPriv.set( this, "focus", ... ) - // dataPriv.set( this, "blur", ... ) - leverageNative( this, type, true ); - - if ( document.documentMode ) { - - // Support: IE 9 - 11+ - // We use the same native handler for focusin & focus (and focusout & blur) - // so we need to coordinate setup & teardown parts between those events. - // Use `delegateType` as the key as `type` is already used by `leverageNative`. - attaches = dataPriv.get( this, delegateType ); - if ( !attaches ) { - this.addEventListener( delegateType, focusMappedHandler ); - } - dataPriv.set( this, delegateType, ( attaches || 0 ) + 1 ); - } else { - - // Return false to allow normal processing in the caller - return false; - } - }, - trigger: function() { - - // Force setup before trigger - leverageNative( this, type ); - - // Return non-false to allow normal event-path propagation - return true; - }, - - teardown: function() { - var attaches; - - if ( document.documentMode ) { - attaches = dataPriv.get( this, delegateType ) - 1; - if ( !attaches ) { - this.removeEventListener( delegateType, focusMappedHandler ); - dataPriv.remove( this, delegateType ); - } else { - dataPriv.set( this, delegateType, attaches ); - } - } else { - - // Return false to indicate standard teardown should be applied - return false; - } - }, - - // Suppress native focus or blur if we're currently inside - // a leveraged native-event stack - _default: function( event ) { - return dataPriv.get( event.target, type ); - }, - - delegateType: delegateType - }; - - // Support: Firefox <=44 - // Firefox doesn't have focus(in | out) events - // Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787 - // - // Support: Chrome <=48 - 49, Safari <=9.0 - 9.1 - // focus(in | out) events fire after focus & blur events, - // which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order - // Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857 - // - // Support: IE 9 - 11+ - // To preserve relative focusin/focus & focusout/blur event order guaranteed on the 3.x branch, - // attach a single handler for both events in IE. - jQuery.event.special[ delegateType ] = { - setup: function() { - - // Handle: regular nodes (via `this.ownerDocument`), window - // (via `this.document`) & document (via `this`). - var doc = this.ownerDocument || this.document || this, - dataHolder = document.documentMode ? this : doc, - attaches = dataPriv.get( dataHolder, delegateType ); - - // Support: IE 9 - 11+ - // We use the same native handler for focusin & focus (and focusout & blur) - // so we need to coordinate setup & teardown parts between those events. - // Use `delegateType` as the key as `type` is already used by `leverageNative`. - if ( !attaches ) { - if ( document.documentMode ) { - this.addEventListener( delegateType, focusMappedHandler ); - } else { - doc.addEventListener( type, focusMappedHandler, true ); - } - } - dataPriv.set( dataHolder, delegateType, ( attaches || 0 ) + 1 ); - }, - teardown: function() { - var doc = this.ownerDocument || this.document || this, - dataHolder = document.documentMode ? this : doc, - attaches = dataPriv.get( dataHolder, delegateType ) - 1; - - if ( !attaches ) { - if ( document.documentMode ) { - this.removeEventListener( delegateType, focusMappedHandler ); - } else { - doc.removeEventListener( type, focusMappedHandler, true ); - } - dataPriv.remove( dataHolder, delegateType ); - } else { - dataPriv.set( dataHolder, delegateType, attaches ); - } - } - }; -} ); - -// Create mouseenter/leave events using mouseover/out and event-time checks -// so that event delegation works in jQuery. -// Do the same for pointerenter/pointerleave and pointerover/pointerout -// -// Support: Safari 7 only -// Safari sends mouseenter too often; see: -// https://bugs.chromium.org/p/chromium/issues/detail?id=470258 -// for the description of the bug (it existed in older Chrome versions as well). -jQuery.each( { - mouseenter: "mouseover", - mouseleave: "mouseout", - pointerenter: "pointerover", - pointerleave: "pointerout" -}, function( orig, fix ) { - jQuery.event.special[ orig ] = { - delegateType: fix, - bindType: fix, - - handle: function( event ) { - var ret, - target = this, - related = event.relatedTarget, - handleObj = event.handleObj; - - // For mouseenter/leave call the handler if related is outside the target. - // NB: No relatedTarget if the mouse left/entered the browser window - if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { - event.type = handleObj.origType; - ret = handleObj.handler.apply( this, arguments ); - event.type = fix; - } - return ret; - } - }; -} ); - -jQuery.fn.extend( { - - on: function( types, selector, data, fn ) { - return on( this, types, selector, data, fn ); - }, - one: function( types, selector, data, fn ) { - return on( this, types, selector, data, fn, 1 ); - }, - off: function( types, selector, fn ) { - var handleObj, type; - if ( types && types.preventDefault && types.handleObj ) { - - // ( event ) dispatched jQuery.Event - handleObj = types.handleObj; - jQuery( types.delegateTarget ).off( - handleObj.namespace ? - handleObj.origType + "." + handleObj.namespace : - handleObj.origType, - handleObj.selector, - handleObj.handler - ); - return this; - } - if ( typeof types === "object" ) { - - // ( types-object [, selector] ) - for ( type in types ) { - this.off( type, selector, types[ type ] ); - } - return this; - } - if ( selector === false || typeof selector === "function" ) { - - // ( types [, fn] ) - fn = selector; - selector = undefined; - } - if ( fn === false ) { - fn = returnFalse; - } - return this.each( function() { - jQuery.event.remove( this, types, fn, selector ); - } ); - } -} ); - - -var - - // Support: IE <=10 - 11, Edge 12 - 13 only - // In IE/Edge using regex groups here causes severe slowdowns. - // See https://connect.microsoft.com/IE/feedback/details/1736512/ - rnoInnerhtml = /\s*$/g; - -// Prefer a tbody over its parent table for containing new rows -function manipulationTarget( elem, content ) { - if ( nodeName( elem, "table" ) && - nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) { - - return jQuery( elem ).children( "tbody" )[ 0 ] || elem; - } - - return elem; -} - -// Replace/restore the type attribute of script elements for safe DOM manipulation -function disableScript( elem ) { - elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type; - return elem; -} -function restoreScript( elem ) { - if ( ( elem.type || "" ).slice( 0, 5 ) === "true/" ) { - elem.type = elem.type.slice( 5 ); - } else { - elem.removeAttribute( "type" ); - } - - return elem; -} - -function cloneCopyEvent( src, dest ) { - var i, l, type, pdataOld, udataOld, udataCur, events; - - if ( dest.nodeType !== 1 ) { - return; - } - - // 1. Copy private data: events, handlers, etc. - if ( dataPriv.hasData( src ) ) { - pdataOld = dataPriv.get( src ); - events = pdataOld.events; - - if ( events ) { - dataPriv.remove( dest, "handle events" ); - - for ( type in events ) { - for ( i = 0, l = events[ type ].length; i < l; i++ ) { - jQuery.event.add( dest, type, events[ type ][ i ] ); - } - } - } - } - - // 2. Copy user data - if ( dataUser.hasData( src ) ) { - udataOld = dataUser.access( src ); - udataCur = jQuery.extend( {}, udataOld ); - - dataUser.set( dest, udataCur ); - } -} - -// Fix IE bugs, see support tests -function fixInput( src, dest ) { - var nodeName = dest.nodeName.toLowerCase(); - - // Fails to persist the checked state of a cloned checkbox or radio button. - if ( nodeName === "input" && rcheckableType.test( src.type ) ) { - dest.checked = src.checked; - - // Fails to return the selected option to the default selected state when cloning options - } else if ( nodeName === "input" || nodeName === "textarea" ) { - dest.defaultValue = src.defaultValue; - } -} - -function domManip( collection, args, callback, ignored ) { - - // Flatten any nested arrays - args = flat( args ); - - var fragment, first, scripts, hasScripts, node, doc, - i = 0, - l = collection.length, - iNoClone = l - 1, - value = args[ 0 ], - valueIsFunction = isFunction( value ); - - // We can't cloneNode fragments that contain checked, in WebKit - if ( valueIsFunction || - ( l > 1 && typeof value === "string" && - !support.checkClone && rchecked.test( value ) ) ) { - return collection.each( function( index ) { - var self = collection.eq( index ); - if ( valueIsFunction ) { - args[ 0 ] = value.call( this, index, self.html() ); - } - domManip( self, args, callback, ignored ); - } ); - } - - if ( l ) { - fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored ); - first = fragment.firstChild; - - if ( fragment.childNodes.length === 1 ) { - fragment = first; - } - - // Require either new content or an interest in ignored elements to invoke the callback - if ( first || ignored ) { - scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); - hasScripts = scripts.length; - - // Use the original fragment for the last item - // instead of the first because it can end up - // being emptied incorrectly in certain situations (trac-8070). - for ( ; i < l; i++ ) { - node = fragment; - - if ( i !== iNoClone ) { - node = jQuery.clone( node, true, true ); - - // Keep references to cloned scripts for later restoration - if ( hasScripts ) { - - // Support: Android <=4.0 only, PhantomJS 1 only - // push.apply(_, arraylike) throws on ancient WebKit - jQuery.merge( scripts, getAll( node, "script" ) ); - } - } - - callback.call( collection[ i ], node, i ); - } - - if ( hasScripts ) { - doc = scripts[ scripts.length - 1 ].ownerDocument; - - // Re-enable scripts - jQuery.map( scripts, restoreScript ); - - // Evaluate executable scripts on first document insertion - for ( i = 0; i < hasScripts; i++ ) { - node = scripts[ i ]; - if ( rscriptType.test( node.type || "" ) && - !dataPriv.access( node, "globalEval" ) && - jQuery.contains( doc, node ) ) { - - if ( node.src && ( node.type || "" ).toLowerCase() !== "module" ) { - - // Optional AJAX dependency, but won't run scripts if not present - if ( jQuery._evalUrl && !node.noModule ) { - jQuery._evalUrl( node.src, { - nonce: node.nonce || node.getAttribute( "nonce" ) - }, doc ); - } - } else { - - // Unwrap a CDATA section containing script contents. This shouldn't be - // needed as in XML documents they're already not visible when - // inspecting element contents and in HTML documents they have no - // meaning but we're preserving that logic for backwards compatibility. - // This will be removed completely in 4.0. See gh-4904. - DOMEval( node.textContent.replace( rcleanScript, "" ), node, doc ); - } - } - } - } - } - } - - return collection; -} - -function remove( elem, selector, keepData ) { - var node, - nodes = selector ? jQuery.filter( selector, elem ) : elem, - i = 0; - - for ( ; ( node = nodes[ i ] ) != null; i++ ) { - if ( !keepData && node.nodeType === 1 ) { - jQuery.cleanData( getAll( node ) ); - } - - if ( node.parentNode ) { - if ( keepData && isAttached( node ) ) { - setGlobalEval( getAll( node, "script" ) ); - } - node.parentNode.removeChild( node ); - } - } - - return elem; -} - -jQuery.extend( { - htmlPrefilter: function( html ) { - return html; - }, - - clone: function( elem, dataAndEvents, deepDataAndEvents ) { - var i, l, srcElements, destElements, - clone = elem.cloneNode( true ), - inPage = isAttached( elem ); - - // Fix IE cloning issues - if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && - !jQuery.isXMLDoc( elem ) ) { - - // We eschew jQuery#find here for performance reasons: - // https://jsperf.com/getall-vs-sizzle/2 - destElements = getAll( clone ); - srcElements = getAll( elem ); - - for ( i = 0, l = srcElements.length; i < l; i++ ) { - fixInput( srcElements[ i ], destElements[ i ] ); - } - } - - // Copy the events from the original to the clone - if ( dataAndEvents ) { - if ( deepDataAndEvents ) { - srcElements = srcElements || getAll( elem ); - destElements = destElements || getAll( clone ); - - for ( i = 0, l = srcElements.length; i < l; i++ ) { - cloneCopyEvent( srcElements[ i ], destElements[ i ] ); - } - } else { - cloneCopyEvent( elem, clone ); - } - } - - // Preserve script evaluation history - destElements = getAll( clone, "script" ); - if ( destElements.length > 0 ) { - setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); - } - - // Return the cloned set - return clone; - }, - - cleanData: function( elems ) { - var data, elem, type, - special = jQuery.event.special, - i = 0; - - for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) { - if ( acceptData( elem ) ) { - if ( ( data = elem[ dataPriv.expando ] ) ) { - if ( data.events ) { - for ( type in data.events ) { - if ( special[ type ] ) { - jQuery.event.remove( elem, type ); - - // This is a shortcut to avoid jQuery.event.remove's overhead - } else { - jQuery.removeEvent( elem, type, data.handle ); - } - } - } - - // Support: Chrome <=35 - 45+ - // Assign undefined instead of using delete, see Data#remove - elem[ dataPriv.expando ] = undefined; - } - if ( elem[ dataUser.expando ] ) { - - // Support: Chrome <=35 - 45+ - // Assign undefined instead of using delete, see Data#remove - elem[ dataUser.expando ] = undefined; - } - } - } - } -} ); - -jQuery.fn.extend( { - detach: function( selector ) { - return remove( this, selector, true ); - }, - - remove: function( selector ) { - return remove( this, selector ); - }, - - text: function( value ) { - return access( this, function( value ) { - return value === undefined ? - jQuery.text( this ) : - this.empty().each( function() { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - this.textContent = value; - } - } ); - }, null, value, arguments.length ); - }, - - append: function() { - return domManip( this, arguments, function( elem ) { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - var target = manipulationTarget( this, elem ); - target.appendChild( elem ); - } - } ); - }, - - prepend: function() { - return domManip( this, arguments, function( elem ) { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - var target = manipulationTarget( this, elem ); - target.insertBefore( elem, target.firstChild ); - } - } ); - }, - - before: function() { - return domManip( this, arguments, function( elem ) { - if ( this.parentNode ) { - this.parentNode.insertBefore( elem, this ); - } - } ); - }, - - after: function() { - return domManip( this, arguments, function( elem ) { - if ( this.parentNode ) { - this.parentNode.insertBefore( elem, this.nextSibling ); - } - } ); - }, - - empty: function() { - var elem, - i = 0; - - for ( ; ( elem = this[ i ] ) != null; i++ ) { - if ( elem.nodeType === 1 ) { - - // Prevent memory leaks - jQuery.cleanData( getAll( elem, false ) ); - - // Remove any remaining nodes - elem.textContent = ""; - } - } - - return this; - }, - - clone: function( dataAndEvents, deepDataAndEvents ) { - dataAndEvents = dataAndEvents == null ? false : dataAndEvents; - deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; - - return this.map( function() { - return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); - } ); - }, - - html: function( value ) { - return access( this, function( value ) { - var elem = this[ 0 ] || {}, - i = 0, - l = this.length; - - if ( value === undefined && elem.nodeType === 1 ) { - return elem.innerHTML; - } - - // See if we can take a shortcut and just use innerHTML - if ( typeof value === "string" && !rnoInnerhtml.test( value ) && - !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { - - value = jQuery.htmlPrefilter( value ); - - try { - for ( ; i < l; i++ ) { - elem = this[ i ] || {}; - - // Remove element nodes and prevent memory leaks - if ( elem.nodeType === 1 ) { - jQuery.cleanData( getAll( elem, false ) ); - elem.innerHTML = value; - } - } - - elem = 0; - - // If using innerHTML throws an exception, use the fallback method - } catch ( e ) {} - } - - if ( elem ) { - this.empty().append( value ); - } - }, null, value, arguments.length ); - }, - - replaceWith: function() { - var ignored = []; - - // Make the changes, replacing each non-ignored context element with the new content - return domManip( this, arguments, function( elem ) { - var parent = this.parentNode; - - if ( jQuery.inArray( this, ignored ) < 0 ) { - jQuery.cleanData( getAll( this ) ); - if ( parent ) { - parent.replaceChild( elem, this ); - } - } - - // Force callback invocation - }, ignored ); - } -} ); - -jQuery.each( { - appendTo: "append", - prependTo: "prepend", - insertBefore: "before", - insertAfter: "after", - replaceAll: "replaceWith" -}, function( name, original ) { - jQuery.fn[ name ] = function( selector ) { - var elems, - ret = [], - insert = jQuery( selector ), - last = insert.length - 1, - i = 0; - - for ( ; i <= last; i++ ) { - elems = i === last ? this : this.clone( true ); - jQuery( insert[ i ] )[ original ]( elems ); - - // Support: Android <=4.0 only, PhantomJS 1 only - // .get() because push.apply(_, arraylike) throws on ancient WebKit - push.apply( ret, elems.get() ); - } - - return this.pushStack( ret ); - }; -} ); -var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ); - -var rcustomProp = /^--/; - - -var getStyles = function( elem ) { - - // Support: IE <=11 only, Firefox <=30 (trac-15098, trac-14150) - // IE throws on elements created in popups - // FF meanwhile throws on frame elements through "defaultView.getComputedStyle" - var view = elem.ownerDocument.defaultView; - - if ( !view || !view.opener ) { - view = window; - } - - return view.getComputedStyle( elem ); - }; - -var swap = function( elem, options, callback ) { - var ret, name, - old = {}; - - // Remember the old values, and insert the new ones - for ( name in options ) { - old[ name ] = elem.style[ name ]; - elem.style[ name ] = options[ name ]; - } - - ret = callback.call( elem ); - - // Revert the old values - for ( name in options ) { - elem.style[ name ] = old[ name ]; - } - - return ret; -}; - - -var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" ); - - - -( function() { - - // Executing both pixelPosition & boxSizingReliable tests require only one layout - // so they're executed at the same time to save the second computation. - function computeStyleTests() { - - // This is a singleton, we need to execute it only once - if ( !div ) { - return; - } - - container.style.cssText = "position:absolute;left:-11111px;width:60px;" + - "margin-top:1px;padding:0;border:0"; - div.style.cssText = - "position:relative;display:block;box-sizing:border-box;overflow:scroll;" + - "margin:auto;border:1px;padding:1px;" + - "width:60%;top:1%"; - documentElement.appendChild( container ).appendChild( div ); - - var divStyle = window.getComputedStyle( div ); - pixelPositionVal = divStyle.top !== "1%"; - - // Support: Android 4.0 - 4.3 only, Firefox <=3 - 44 - reliableMarginLeftVal = roundPixelMeasures( divStyle.marginLeft ) === 12; - - // Support: Android 4.0 - 4.3 only, Safari <=9.1 - 10.1, iOS <=7.0 - 9.3 - // Some styles come back with percentage values, even though they shouldn't - div.style.right = "60%"; - pixelBoxStylesVal = roundPixelMeasures( divStyle.right ) === 36; - - // Support: IE 9 - 11 only - // Detect misreporting of content dimensions for box-sizing:border-box elements - boxSizingReliableVal = roundPixelMeasures( divStyle.width ) === 36; - - // Support: IE 9 only - // Detect overflow:scroll screwiness (gh-3699) - // Support: Chrome <=64 - // Don't get tricked when zoom affects offsetWidth (gh-4029) - div.style.position = "absolute"; - scrollboxSizeVal = roundPixelMeasures( div.offsetWidth / 3 ) === 12; - - documentElement.removeChild( container ); - - // Nullify the div so it wouldn't be stored in the memory and - // it will also be a sign that checks already performed - div = null; - } - - function roundPixelMeasures( measure ) { - return Math.round( parseFloat( measure ) ); - } - - var pixelPositionVal, boxSizingReliableVal, scrollboxSizeVal, pixelBoxStylesVal, - reliableTrDimensionsVal, reliableMarginLeftVal, - container = document.createElement( "div" ), - div = document.createElement( "div" ); - - // Finish early in limited (non-browser) environments - if ( !div.style ) { - return; - } - - // Support: IE <=9 - 11 only - // Style of cloned element affects source element cloned (trac-8908) - div.style.backgroundClip = "content-box"; - div.cloneNode( true ).style.backgroundClip = ""; - support.clearCloneStyle = div.style.backgroundClip === "content-box"; - - jQuery.extend( support, { - boxSizingReliable: function() { - computeStyleTests(); - return boxSizingReliableVal; - }, - pixelBoxStyles: function() { - computeStyleTests(); - return pixelBoxStylesVal; - }, - pixelPosition: function() { - computeStyleTests(); - return pixelPositionVal; - }, - reliableMarginLeft: function() { - computeStyleTests(); - return reliableMarginLeftVal; - }, - scrollboxSize: function() { - computeStyleTests(); - return scrollboxSizeVal; - }, - - // Support: IE 9 - 11+, Edge 15 - 18+ - // IE/Edge misreport `getComputedStyle` of table rows with width/height - // set in CSS while `offset*` properties report correct values. - // Behavior in IE 9 is more subtle than in newer versions & it passes - // some versions of this test; make sure not to make it pass there! - // - // Support: Firefox 70+ - // Only Firefox includes border widths - // in computed dimensions. (gh-4529) - reliableTrDimensions: function() { - var table, tr, trChild, trStyle; - if ( reliableTrDimensionsVal == null ) { - table = document.createElement( "table" ); - tr = document.createElement( "tr" ); - trChild = document.createElement( "div" ); - - table.style.cssText = "position:absolute;left:-11111px;border-collapse:separate"; - tr.style.cssText = "box-sizing:content-box;border:1px solid"; - - // Support: Chrome 86+ - // Height set through cssText does not get applied. - // Computed height then comes back as 0. - tr.style.height = "1px"; - trChild.style.height = "9px"; - - // Support: Android 8 Chrome 86+ - // In our bodyBackground.html iframe, - // display for all div elements is set to "inline", - // which causes a problem only in Android 8 Chrome 86. - // Ensuring the div is `display: block` - // gets around this issue. - trChild.style.display = "block"; - - documentElement - .appendChild( table ) - .appendChild( tr ) - .appendChild( trChild ); - - trStyle = window.getComputedStyle( tr ); - reliableTrDimensionsVal = ( parseInt( trStyle.height, 10 ) + - parseInt( trStyle.borderTopWidth, 10 ) + - parseInt( trStyle.borderBottomWidth, 10 ) ) === tr.offsetHeight; - - documentElement.removeChild( table ); - } - return reliableTrDimensionsVal; - } - } ); -} )(); - - -function curCSS( elem, name, computed ) { - var width, minWidth, maxWidth, ret, - isCustomProp = rcustomProp.test( name ), - - // Support: Firefox 51+ - // Retrieving style before computed somehow - // fixes an issue with getting wrong values - // on detached elements - style = elem.style; - - computed = computed || getStyles( elem ); - - // getPropertyValue is needed for: - // .css('filter') (IE 9 only, trac-12537) - // .css('--customProperty) (gh-3144) - if ( computed ) { - - // Support: IE <=9 - 11+ - // IE only supports `"float"` in `getPropertyValue`; in computed styles - // it's only available as `"cssFloat"`. We no longer modify properties - // sent to `.css()` apart from camelCasing, so we need to check both. - // Normally, this would create difference in behavior: if - // `getPropertyValue` returns an empty string, the value returned - // by `.css()` would be `undefined`. This is usually the case for - // disconnected elements. However, in IE even disconnected elements - // with no styles return `"none"` for `getPropertyValue( "float" )` - ret = computed.getPropertyValue( name ) || computed[ name ]; - - if ( isCustomProp && ret ) { - - // Support: Firefox 105+, Chrome <=105+ - // Spec requires trimming whitespace for custom properties (gh-4926). - // Firefox only trims leading whitespace. Chrome just collapses - // both leading & trailing whitespace to a single space. - // - // Fall back to `undefined` if empty string returned. - // This collapses a missing definition with property defined - // and set to an empty string but there's no standard API - // allowing us to differentiate them without a performance penalty - // and returning `undefined` aligns with older jQuery. - // - // rtrimCSS treats U+000D CARRIAGE RETURN and U+000C FORM FEED - // as whitespace while CSS does not, but this is not a problem - // because CSS preprocessing replaces them with U+000A LINE FEED - // (which *is* CSS whitespace) - // https://www.w3.org/TR/css-syntax-3/#input-preprocessing - ret = ret.replace( rtrimCSS, "$1" ) || undefined; - } - - if ( ret === "" && !isAttached( elem ) ) { - ret = jQuery.style( elem, name ); - } - - // A tribute to the "awesome hack by Dean Edwards" - // Android Browser returns percentage for some values, - // but width seems to be reliably pixels. - // This is against the CSSOM draft spec: - // https://drafts.csswg.org/cssom/#resolved-values - if ( !support.pixelBoxStyles() && rnumnonpx.test( ret ) && rboxStyle.test( name ) ) { - - // Remember the original values - width = style.width; - minWidth = style.minWidth; - maxWidth = style.maxWidth; - - // Put in the new values to get a computed value out - style.minWidth = style.maxWidth = style.width = ret; - ret = computed.width; - - // Revert the changed values - style.width = width; - style.minWidth = minWidth; - style.maxWidth = maxWidth; - } - } - - return ret !== undefined ? - - // Support: IE <=9 - 11 only - // IE returns zIndex value as an integer. - ret + "" : - ret; -} - - -function addGetHookIf( conditionFn, hookFn ) { - - // Define the hook, we'll check on the first run if it's really needed. - return { - get: function() { - if ( conditionFn() ) { - - // Hook not needed (or it's not possible to use it due - // to missing dependency), remove it. - delete this.get; - return; - } - - // Hook needed; redefine it so that the support test is not executed again. - return ( this.get = hookFn ).apply( this, arguments ); - } - }; -} - - -var cssPrefixes = [ "Webkit", "Moz", "ms" ], - emptyStyle = document.createElement( "div" ).style, - vendorProps = {}; - -// Return a vendor-prefixed property or undefined -function vendorPropName( name ) { - - // Check for vendor prefixed names - var capName = name[ 0 ].toUpperCase() + name.slice( 1 ), - i = cssPrefixes.length; - - while ( i-- ) { - name = cssPrefixes[ i ] + capName; - if ( name in emptyStyle ) { - return name; - } - } -} - -// Return a potentially-mapped jQuery.cssProps or vendor prefixed property -function finalPropName( name ) { - var final = jQuery.cssProps[ name ] || vendorProps[ name ]; - - if ( final ) { - return final; - } - if ( name in emptyStyle ) { - return name; - } - return vendorProps[ name ] = vendorPropName( name ) || name; -} - - -var - - // Swappable if display is none or starts with table - // except "table", "table-cell", or "table-caption" - // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display - rdisplayswap = /^(none|table(?!-c[ea]).+)/, - cssShow = { position: "absolute", visibility: "hidden", display: "block" }, - cssNormalTransform = { - letterSpacing: "0", - fontWeight: "400" - }; - -function setPositiveNumber( _elem, value, subtract ) { - - // Any relative (+/-) values have already been - // normalized at this point - var matches = rcssNum.exec( value ); - return matches ? - - // Guard against undefined "subtract", e.g., when used as in cssHooks - Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) : - value; -} - -function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) { - var i = dimension === "width" ? 1 : 0, - extra = 0, - delta = 0, - marginDelta = 0; - - // Adjustment may not be necessary - if ( box === ( isBorderBox ? "border" : "content" ) ) { - return 0; - } - - for ( ; i < 4; i += 2 ) { - - // Both box models exclude margin - // Count margin delta separately to only add it after scroll gutter adjustment. - // This is needed to make negative margins work with `outerHeight( true )` (gh-3982). - if ( box === "margin" ) { - marginDelta += jQuery.css( elem, box + cssExpand[ i ], true, styles ); - } - - // If we get here with a content-box, we're seeking "padding" or "border" or "margin" - if ( !isBorderBox ) { - - // Add padding - delta += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); - - // For "border" or "margin", add border - if ( box !== "padding" ) { - delta += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); - - // But still keep track of it otherwise - } else { - extra += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); - } - - // If we get here with a border-box (content + padding + border), we're seeking "content" or - // "padding" or "margin" - } else { - - // For "content", subtract padding - if ( box === "content" ) { - delta -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); - } - - // For "content" or "padding", subtract border - if ( box !== "margin" ) { - delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); - } - } - } - - // Account for positive content-box scroll gutter when requested by providing computedVal - if ( !isBorderBox && computedVal >= 0 ) { - - // offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border - // Assuming integer scroll gutter, subtract the rest and round down - delta += Math.max( 0, Math.ceil( - elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - - computedVal - - delta - - extra - - 0.5 - - // If offsetWidth/offsetHeight is unknown, then we can't determine content-box scroll gutter - // Use an explicit zero to avoid NaN (gh-3964) - ) ) || 0; - } - - return delta + marginDelta; -} - -function getWidthOrHeight( elem, dimension, extra ) { - - // Start with computed style - var styles = getStyles( elem ), - - // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-4322). - // Fake content-box until we know it's needed to know the true value. - boxSizingNeeded = !support.boxSizingReliable() || extra, - isBorderBox = boxSizingNeeded && - jQuery.css( elem, "boxSizing", false, styles ) === "border-box", - valueIsBorderBox = isBorderBox, - - val = curCSS( elem, dimension, styles ), - offsetProp = "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ); - - // Support: Firefox <=54 - // Return a confounding non-pixel value or feign ignorance, as appropriate. - if ( rnumnonpx.test( val ) ) { - if ( !extra ) { - return val; - } - val = "auto"; - } - - - // Support: IE 9 - 11 only - // Use offsetWidth/offsetHeight for when box sizing is unreliable. - // In those cases, the computed value can be trusted to be border-box. - if ( ( !support.boxSizingReliable() && isBorderBox || - - // Support: IE 10 - 11+, Edge 15 - 18+ - // IE/Edge misreport `getComputedStyle` of table rows with width/height - // set in CSS while `offset*` properties report correct values. - // Interestingly, in some cases IE 9 doesn't suffer from this issue. - !support.reliableTrDimensions() && nodeName( elem, "tr" ) || - - // Fall back to offsetWidth/offsetHeight when value is "auto" - // This happens for inline elements with no explicit setting (gh-3571) - val === "auto" || - - // Support: Android <=4.1 - 4.3 only - // Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602) - !parseFloat( val ) && jQuery.css( elem, "display", false, styles ) === "inline" ) && - - // Make sure the element is visible & connected - elem.getClientRects().length ) { - - isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; - - // Where available, offsetWidth/offsetHeight approximate border box dimensions. - // Where not available (e.g., SVG), assume unreliable box-sizing and interpret the - // retrieved value as a content box dimension. - valueIsBorderBox = offsetProp in elem; - if ( valueIsBorderBox ) { - val = elem[ offsetProp ]; - } - } - - // Normalize "" and auto - val = parseFloat( val ) || 0; - - // Adjust for the element's box model - return ( val + - boxModelAdjustment( - elem, - dimension, - extra || ( isBorderBox ? "border" : "content" ), - valueIsBorderBox, - styles, - - // Provide the current computed size to request scroll gutter calculation (gh-3589) - val - ) - ) + "px"; -} - -jQuery.extend( { - - // Add in style property hooks for overriding the default - // behavior of getting and setting a style property - cssHooks: { - opacity: { - get: function( elem, computed ) { - if ( computed ) { - - // We should always get a number back from opacity - var ret = curCSS( elem, "opacity" ); - return ret === "" ? "1" : ret; - } - } - } - }, - - // Don't automatically add "px" to these possibly-unitless properties - cssNumber: { - animationIterationCount: true, - aspectRatio: true, - borderImageSlice: true, - columnCount: true, - flexGrow: true, - flexShrink: true, - fontWeight: true, - gridArea: true, - gridColumn: true, - gridColumnEnd: true, - gridColumnStart: true, - gridRow: true, - gridRowEnd: true, - gridRowStart: true, - lineHeight: true, - opacity: true, - order: true, - orphans: true, - scale: true, - widows: true, - zIndex: true, - zoom: true, - - // SVG-related - fillOpacity: true, - floodOpacity: true, - stopOpacity: true, - strokeMiterlimit: true, - strokeOpacity: true - }, - - // Add in properties whose names you wish to fix before - // setting or getting the value - cssProps: {}, - - // Get and set the style property on a DOM Node - style: function( elem, name, value, extra ) { - - // Don't set styles on text and comment nodes - if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { - return; - } - - // Make sure that we're working with the right name - var ret, type, hooks, - origName = camelCase( name ), - isCustomProp = rcustomProp.test( name ), - style = elem.style; - - // Make sure that we're working with the right name. We don't - // want to query the value if it is a CSS custom property - // since they are user-defined. - if ( !isCustomProp ) { - name = finalPropName( origName ); - } - - // Gets hook for the prefixed version, then unprefixed version - hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; - - // Check if we're setting a value - if ( value !== undefined ) { - type = typeof value; - - // Convert "+=" or "-=" to relative numbers (trac-7345) - if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) { - value = adjustCSS( elem, name, ret ); - - // Fixes bug trac-9237 - type = "number"; - } - - // Make sure that null and NaN values aren't set (trac-7116) - if ( value == null || value !== value ) { - return; - } - - // If a number was passed in, add the unit (except for certain CSS properties) - // The isCustomProp check can be removed in jQuery 4.0 when we only auto-append - // "px" to a few hardcoded values. - if ( type === "number" && !isCustomProp ) { - value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" ); - } - - // background-* props affect original clone's values - if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) { - style[ name ] = "inherit"; - } - - // If a hook was provided, use that value, otherwise just set the specified value - if ( !hooks || !( "set" in hooks ) || - ( value = hooks.set( elem, value, extra ) ) !== undefined ) { - - if ( isCustomProp ) { - style.setProperty( name, value ); - } else { - style[ name ] = value; - } - } - - } else { - - // If a hook was provided get the non-computed value from there - if ( hooks && "get" in hooks && - ( ret = hooks.get( elem, false, extra ) ) !== undefined ) { - - return ret; - } - - // Otherwise just get the value from the style object - return style[ name ]; - } - }, - - css: function( elem, name, extra, styles ) { - var val, num, hooks, - origName = camelCase( name ), - isCustomProp = rcustomProp.test( name ); - - // Make sure that we're working with the right name. We don't - // want to modify the value if it is a CSS custom property - // since they are user-defined. - if ( !isCustomProp ) { - name = finalPropName( origName ); - } - - // Try prefixed name followed by the unprefixed name - hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; - - // If a hook was provided get the computed value from there - if ( hooks && "get" in hooks ) { - val = hooks.get( elem, true, extra ); - } - - // Otherwise, if a way to get the computed value exists, use that - if ( val === undefined ) { - val = curCSS( elem, name, styles ); - } - - // Convert "normal" to computed value - if ( val === "normal" && name in cssNormalTransform ) { - val = cssNormalTransform[ name ]; - } - - // Make numeric if forced or a qualifier was provided and val looks numeric - if ( extra === "" || extra ) { - num = parseFloat( val ); - return extra === true || isFinite( num ) ? num || 0 : val; - } - - return val; - } -} ); - -jQuery.each( [ "height", "width" ], function( _i, dimension ) { - jQuery.cssHooks[ dimension ] = { - get: function( elem, computed, extra ) { - if ( computed ) { - - // Certain elements can have dimension info if we invisibly show them - // but it must have a current display style that would benefit - return rdisplayswap.test( jQuery.css( elem, "display" ) ) && - - // Support: Safari 8+ - // Table columns in Safari have non-zero offsetWidth & zero - // getBoundingClientRect().width unless display is changed. - // Support: IE <=11 only - // Running getBoundingClientRect on a disconnected node - // in IE throws an error. - ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ? - swap( elem, cssShow, function() { - return getWidthOrHeight( elem, dimension, extra ); - } ) : - getWidthOrHeight( elem, dimension, extra ); - } - }, - - set: function( elem, value, extra ) { - var matches, - styles = getStyles( elem ), - - // Only read styles.position if the test has a chance to fail - // to avoid forcing a reflow. - scrollboxSizeBuggy = !support.scrollboxSize() && - styles.position === "absolute", - - // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-3991) - boxSizingNeeded = scrollboxSizeBuggy || extra, - isBorderBox = boxSizingNeeded && - jQuery.css( elem, "boxSizing", false, styles ) === "border-box", - subtract = extra ? - boxModelAdjustment( - elem, - dimension, - extra, - isBorderBox, - styles - ) : - 0; - - // Account for unreliable border-box dimensions by comparing offset* to computed and - // faking a content-box to get border and padding (gh-3699) - if ( isBorderBox && scrollboxSizeBuggy ) { - subtract -= Math.ceil( - elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - - parseFloat( styles[ dimension ] ) - - boxModelAdjustment( elem, dimension, "border", false, styles ) - - 0.5 - ); - } - - // Convert to pixels if value adjustment is needed - if ( subtract && ( matches = rcssNum.exec( value ) ) && - ( matches[ 3 ] || "px" ) !== "px" ) { - - elem.style[ dimension ] = value; - value = jQuery.css( elem, dimension ); - } - - return setPositiveNumber( elem, value, subtract ); - } - }; -} ); - -jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft, - function( elem, computed ) { - if ( computed ) { - return ( parseFloat( curCSS( elem, "marginLeft" ) ) || - elem.getBoundingClientRect().left - - swap( elem, { marginLeft: 0 }, function() { - return elem.getBoundingClientRect().left; - } ) - ) + "px"; - } - } -); - -// These hooks are used by animate to expand properties -jQuery.each( { - margin: "", - padding: "", - border: "Width" -}, function( prefix, suffix ) { - jQuery.cssHooks[ prefix + suffix ] = { - expand: function( value ) { - var i = 0, - expanded = {}, - - // Assumes a single number if not a string - parts = typeof value === "string" ? value.split( " " ) : [ value ]; - - for ( ; i < 4; i++ ) { - expanded[ prefix + cssExpand[ i ] + suffix ] = - parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; - } - - return expanded; - } - }; - - if ( prefix !== "margin" ) { - jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber; - } -} ); - -jQuery.fn.extend( { - css: function( name, value ) { - return access( this, function( elem, name, value ) { - var styles, len, - map = {}, - i = 0; - - if ( Array.isArray( name ) ) { - styles = getStyles( elem ); - len = name.length; - - for ( ; i < len; i++ ) { - map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); - } - - return map; - } - - return value !== undefined ? - jQuery.style( elem, name, value ) : - jQuery.css( elem, name ); - }, name, value, arguments.length > 1 ); - } -} ); - - -function Tween( elem, options, prop, end, easing ) { - return new Tween.prototype.init( elem, options, prop, end, easing ); -} -jQuery.Tween = Tween; - -Tween.prototype = { - constructor: Tween, - init: function( elem, options, prop, end, easing, unit ) { - this.elem = elem; - this.prop = prop; - this.easing = easing || jQuery.easing._default; - this.options = options; - this.start = this.now = this.cur(); - this.end = end; - this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); - }, - cur: function() { - var hooks = Tween.propHooks[ this.prop ]; - - return hooks && hooks.get ? - hooks.get( this ) : - Tween.propHooks._default.get( this ); - }, - run: function( percent ) { - var eased, - hooks = Tween.propHooks[ this.prop ]; - - if ( this.options.duration ) { - this.pos = eased = jQuery.easing[ this.easing ]( - percent, this.options.duration * percent, 0, 1, this.options.duration - ); - } else { - this.pos = eased = percent; - } - this.now = ( this.end - this.start ) * eased + this.start; - - if ( this.options.step ) { - this.options.step.call( this.elem, this.now, this ); - } - - if ( hooks && hooks.set ) { - hooks.set( this ); - } else { - Tween.propHooks._default.set( this ); - } - return this; - } -}; - -Tween.prototype.init.prototype = Tween.prototype; - -Tween.propHooks = { - _default: { - get: function( tween ) { - var result; - - // Use a property on the element directly when it is not a DOM element, - // or when there is no matching style property that exists. - if ( tween.elem.nodeType !== 1 || - tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) { - return tween.elem[ tween.prop ]; - } - - // Passing an empty string as a 3rd parameter to .css will automatically - // attempt a parseFloat and fallback to a string if the parse fails. - // Simple values such as "10px" are parsed to Float; - // complex values such as "rotate(1rad)" are returned as-is. - result = jQuery.css( tween.elem, tween.prop, "" ); - - // Empty strings, null, undefined and "auto" are converted to 0. - return !result || result === "auto" ? 0 : result; - }, - set: function( tween ) { - - // Use step hook for back compat. - // Use cssHook if its there. - // Use .style if available and use plain properties where available. - if ( jQuery.fx.step[ tween.prop ] ) { - jQuery.fx.step[ tween.prop ]( tween ); - } else if ( tween.elem.nodeType === 1 && ( - jQuery.cssHooks[ tween.prop ] || - tween.elem.style[ finalPropName( tween.prop ) ] != null ) ) { - jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); - } else { - tween.elem[ tween.prop ] = tween.now; - } - } - } -}; - -// Support: IE <=9 only -// Panic based approach to setting things on disconnected nodes -Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { - set: function( tween ) { - if ( tween.elem.nodeType && tween.elem.parentNode ) { - tween.elem[ tween.prop ] = tween.now; - } - } -}; - -jQuery.easing = { - linear: function( p ) { - return p; - }, - swing: function( p ) { - return 0.5 - Math.cos( p * Math.PI ) / 2; - }, - _default: "swing" -}; - -jQuery.fx = Tween.prototype.init; - -// Back compat <1.8 extension point -jQuery.fx.step = {}; - - - - -var - fxNow, inProgress, - rfxtypes = /^(?:toggle|show|hide)$/, - rrun = /queueHooks$/; - -function schedule() { - if ( inProgress ) { - if ( document.hidden === false && window.requestAnimationFrame ) { - window.requestAnimationFrame( schedule ); - } else { - window.setTimeout( schedule, jQuery.fx.interval ); - } - - jQuery.fx.tick(); - } -} - -// Animations created synchronously will run synchronously -function createFxNow() { - window.setTimeout( function() { - fxNow = undefined; - } ); - return ( fxNow = Date.now() ); -} - -// Generate parameters to create a standard animation -function genFx( type, includeWidth ) { - var which, - i = 0, - attrs = { height: type }; - - // If we include width, step value is 1 to do all cssExpand values, - // otherwise step value is 2 to skip over Left and Right - includeWidth = includeWidth ? 1 : 0; - for ( ; i < 4; i += 2 - includeWidth ) { - which = cssExpand[ i ]; - attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; - } - - if ( includeWidth ) { - attrs.opacity = attrs.width = type; - } - - return attrs; -} - -function createTween( value, prop, animation ) { - var tween, - collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ), - index = 0, - length = collection.length; - for ( ; index < length; index++ ) { - if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) { - - // We're done with this property - return tween; - } - } -} - -function defaultPrefilter( elem, props, opts ) { - var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display, - isBox = "width" in props || "height" in props, - anim = this, - orig = {}, - style = elem.style, - hidden = elem.nodeType && isHiddenWithinTree( elem ), - dataShow = dataPriv.get( elem, "fxshow" ); - - // Queue-skipping animations hijack the fx hooks - if ( !opts.queue ) { - hooks = jQuery._queueHooks( elem, "fx" ); - if ( hooks.unqueued == null ) { - hooks.unqueued = 0; - oldfire = hooks.empty.fire; - hooks.empty.fire = function() { - if ( !hooks.unqueued ) { - oldfire(); - } - }; - } - hooks.unqueued++; - - anim.always( function() { - - // Ensure the complete handler is called before this completes - anim.always( function() { - hooks.unqueued--; - if ( !jQuery.queue( elem, "fx" ).length ) { - hooks.empty.fire(); - } - } ); - } ); - } - - // Detect show/hide animations - for ( prop in props ) { - value = props[ prop ]; - if ( rfxtypes.test( value ) ) { - delete props[ prop ]; - toggle = toggle || value === "toggle"; - if ( value === ( hidden ? "hide" : "show" ) ) { - - // Pretend to be hidden if this is a "show" and - // there is still data from a stopped show/hide - if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) { - hidden = true; - - // Ignore all other no-op show/hide data - } else { - continue; - } - } - orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop ); - } - } - - // Bail out if this is a no-op like .hide().hide() - propTween = !jQuery.isEmptyObject( props ); - if ( !propTween && jQuery.isEmptyObject( orig ) ) { - return; - } - - // Restrict "overflow" and "display" styles during box animations - if ( isBox && elem.nodeType === 1 ) { - - // Support: IE <=9 - 11, Edge 12 - 15 - // Record all 3 overflow attributes because IE does not infer the shorthand - // from identically-valued overflowX and overflowY and Edge just mirrors - // the overflowX value there. - opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; - - // Identify a display type, preferring old show/hide data over the CSS cascade - restoreDisplay = dataShow && dataShow.display; - if ( restoreDisplay == null ) { - restoreDisplay = dataPriv.get( elem, "display" ); - } - display = jQuery.css( elem, "display" ); - if ( display === "none" ) { - if ( restoreDisplay ) { - display = restoreDisplay; - } else { - - // Get nonempty value(s) by temporarily forcing visibility - showHide( [ elem ], true ); - restoreDisplay = elem.style.display || restoreDisplay; - display = jQuery.css( elem, "display" ); - showHide( [ elem ] ); - } - } - - // Animate inline elements as inline-block - if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) { - if ( jQuery.css( elem, "float" ) === "none" ) { - - // Restore the original display value at the end of pure show/hide animations - if ( !propTween ) { - anim.done( function() { - style.display = restoreDisplay; - } ); - if ( restoreDisplay == null ) { - display = style.display; - restoreDisplay = display === "none" ? "" : display; - } - } - style.display = "inline-block"; - } - } - } - - if ( opts.overflow ) { - style.overflow = "hidden"; - anim.always( function() { - style.overflow = opts.overflow[ 0 ]; - style.overflowX = opts.overflow[ 1 ]; - style.overflowY = opts.overflow[ 2 ]; - } ); - } - - // Implement show/hide animations - propTween = false; - for ( prop in orig ) { - - // General show/hide setup for this element animation - if ( !propTween ) { - if ( dataShow ) { - if ( "hidden" in dataShow ) { - hidden = dataShow.hidden; - } - } else { - dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } ); - } - - // Store hidden/visible for toggle so `.stop().toggle()` "reverses" - if ( toggle ) { - dataShow.hidden = !hidden; - } - - // Show elements before animating them - if ( hidden ) { - showHide( [ elem ], true ); - } - - /* eslint-disable no-loop-func */ - - anim.done( function() { - - /* eslint-enable no-loop-func */ - - // The final step of a "hide" animation is actually hiding the element - if ( !hidden ) { - showHide( [ elem ] ); - } - dataPriv.remove( elem, "fxshow" ); - for ( prop in orig ) { - jQuery.style( elem, prop, orig[ prop ] ); - } - } ); - } - - // Per-property setup - propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim ); - if ( !( prop in dataShow ) ) { - dataShow[ prop ] = propTween.start; - if ( hidden ) { - propTween.end = propTween.start; - propTween.start = 0; - } - } - } -} - -function propFilter( props, specialEasing ) { - var index, name, easing, value, hooks; - - // camelCase, specialEasing and expand cssHook pass - for ( index in props ) { - name = camelCase( index ); - easing = specialEasing[ name ]; - value = props[ index ]; - if ( Array.isArray( value ) ) { - easing = value[ 1 ]; - value = props[ index ] = value[ 0 ]; - } - - if ( index !== name ) { - props[ name ] = value; - delete props[ index ]; - } - - hooks = jQuery.cssHooks[ name ]; - if ( hooks && "expand" in hooks ) { - value = hooks.expand( value ); - delete props[ name ]; - - // Not quite $.extend, this won't overwrite existing keys. - // Reusing 'index' because we have the correct "name" - for ( index in value ) { - if ( !( index in props ) ) { - props[ index ] = value[ index ]; - specialEasing[ index ] = easing; - } - } - } else { - specialEasing[ name ] = easing; - } - } -} - -function Animation( elem, properties, options ) { - var result, - stopped, - index = 0, - length = Animation.prefilters.length, - deferred = jQuery.Deferred().always( function() { - - // Don't match elem in the :animated selector - delete tick.elem; - } ), - tick = function() { - if ( stopped ) { - return false; - } - var currentTime = fxNow || createFxNow(), - remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), - - // Support: Android 2.3 only - // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (trac-12497) - temp = remaining / animation.duration || 0, - percent = 1 - temp, - index = 0, - length = animation.tweens.length; - - for ( ; index < length; index++ ) { - animation.tweens[ index ].run( percent ); - } - - deferred.notifyWith( elem, [ animation, percent, remaining ] ); - - // If there's more to do, yield - if ( percent < 1 && length ) { - return remaining; - } - - // If this was an empty animation, synthesize a final progress notification - if ( !length ) { - deferred.notifyWith( elem, [ animation, 1, 0 ] ); - } - - // Resolve the animation and report its conclusion - deferred.resolveWith( elem, [ animation ] ); - return false; - }, - animation = deferred.promise( { - elem: elem, - props: jQuery.extend( {}, properties ), - opts: jQuery.extend( true, { - specialEasing: {}, - easing: jQuery.easing._default - }, options ), - originalProperties: properties, - originalOptions: options, - startTime: fxNow || createFxNow(), - duration: options.duration, - tweens: [], - createTween: function( prop, end ) { - var tween = jQuery.Tween( elem, animation.opts, prop, end, - animation.opts.specialEasing[ prop ] || animation.opts.easing ); - animation.tweens.push( tween ); - return tween; - }, - stop: function( gotoEnd ) { - var index = 0, - - // If we are going to the end, we want to run all the tweens - // otherwise we skip this part - length = gotoEnd ? animation.tweens.length : 0; - if ( stopped ) { - return this; - } - stopped = true; - for ( ; index < length; index++ ) { - animation.tweens[ index ].run( 1 ); - } - - // Resolve when we played the last frame; otherwise, reject - if ( gotoEnd ) { - deferred.notifyWith( elem, [ animation, 1, 0 ] ); - deferred.resolveWith( elem, [ animation, gotoEnd ] ); - } else { - deferred.rejectWith( elem, [ animation, gotoEnd ] ); - } - return this; - } - } ), - props = animation.props; - - propFilter( props, animation.opts.specialEasing ); - - for ( ; index < length; index++ ) { - result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts ); - if ( result ) { - if ( isFunction( result.stop ) ) { - jQuery._queueHooks( animation.elem, animation.opts.queue ).stop = - result.stop.bind( result ); - } - return result; - } - } - - jQuery.map( props, createTween, animation ); - - if ( isFunction( animation.opts.start ) ) { - animation.opts.start.call( elem, animation ); - } - - // Attach callbacks from options - animation - .progress( animation.opts.progress ) - .done( animation.opts.done, animation.opts.complete ) - .fail( animation.opts.fail ) - .always( animation.opts.always ); - - jQuery.fx.timer( - jQuery.extend( tick, { - elem: elem, - anim: animation, - queue: animation.opts.queue - } ) - ); - - return animation; -} - -jQuery.Animation = jQuery.extend( Animation, { - - tweeners: { - "*": [ function( prop, value ) { - var tween = this.createTween( prop, value ); - adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween ); - return tween; - } ] - }, - - tweener: function( props, callback ) { - if ( isFunction( props ) ) { - callback = props; - props = [ "*" ]; - } else { - props = props.match( rnothtmlwhite ); - } - - var prop, - index = 0, - length = props.length; - - for ( ; index < length; index++ ) { - prop = props[ index ]; - Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || []; - Animation.tweeners[ prop ].unshift( callback ); - } - }, - - prefilters: [ defaultPrefilter ], - - prefilter: function( callback, prepend ) { - if ( prepend ) { - Animation.prefilters.unshift( callback ); - } else { - Animation.prefilters.push( callback ); - } - } -} ); - -jQuery.speed = function( speed, easing, fn ) { - var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { - complete: fn || !fn && easing || - isFunction( speed ) && speed, - duration: speed, - easing: fn && easing || easing && !isFunction( easing ) && easing - }; - - // Go to the end state if fx are off - if ( jQuery.fx.off ) { - opt.duration = 0; - - } else { - if ( typeof opt.duration !== "number" ) { - if ( opt.duration in jQuery.fx.speeds ) { - opt.duration = jQuery.fx.speeds[ opt.duration ]; - - } else { - opt.duration = jQuery.fx.speeds._default; - } - } - } - - // Normalize opt.queue - true/undefined/null -> "fx" - if ( opt.queue == null || opt.queue === true ) { - opt.queue = "fx"; - } - - // Queueing - opt.old = opt.complete; - - opt.complete = function() { - if ( isFunction( opt.old ) ) { - opt.old.call( this ); - } - - if ( opt.queue ) { - jQuery.dequeue( this, opt.queue ); - } - }; - - return opt; -}; - -jQuery.fn.extend( { - fadeTo: function( speed, to, easing, callback ) { - - // Show any hidden elements after setting opacity to 0 - return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show() - - // Animate to the value specified - .end().animate( { opacity: to }, speed, easing, callback ); - }, - animate: function( prop, speed, easing, callback ) { - var empty = jQuery.isEmptyObject( prop ), - optall = jQuery.speed( speed, easing, callback ), - doAnimation = function() { - - // Operate on a copy of prop so per-property easing won't be lost - var anim = Animation( this, jQuery.extend( {}, prop ), optall ); - - // Empty animations, or finishing resolves immediately - if ( empty || dataPriv.get( this, "finish" ) ) { - anim.stop( true ); - } - }; - - doAnimation.finish = doAnimation; - - return empty || optall.queue === false ? - this.each( doAnimation ) : - this.queue( optall.queue, doAnimation ); - }, - stop: function( type, clearQueue, gotoEnd ) { - var stopQueue = function( hooks ) { - var stop = hooks.stop; - delete hooks.stop; - stop( gotoEnd ); - }; - - if ( typeof type !== "string" ) { - gotoEnd = clearQueue; - clearQueue = type; - type = undefined; - } - if ( clearQueue ) { - this.queue( type || "fx", [] ); - } - - return this.each( function() { - var dequeue = true, - index = type != null && type + "queueHooks", - timers = jQuery.timers, - data = dataPriv.get( this ); - - if ( index ) { - if ( data[ index ] && data[ index ].stop ) { - stopQueue( data[ index ] ); - } - } else { - for ( index in data ) { - if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { - stopQueue( data[ index ] ); - } - } - } - - for ( index = timers.length; index--; ) { - if ( timers[ index ].elem === this && - ( type == null || timers[ index ].queue === type ) ) { - - timers[ index ].anim.stop( gotoEnd ); - dequeue = false; - timers.splice( index, 1 ); - } - } - - // Start the next in the queue if the last step wasn't forced. - // Timers currently will call their complete callbacks, which - // will dequeue but only if they were gotoEnd. - if ( dequeue || !gotoEnd ) { - jQuery.dequeue( this, type ); - } - } ); - }, - finish: function( type ) { - if ( type !== false ) { - type = type || "fx"; - } - return this.each( function() { - var index, - data = dataPriv.get( this ), - queue = data[ type + "queue" ], - hooks = data[ type + "queueHooks" ], - timers = jQuery.timers, - length = queue ? queue.length : 0; - - // Enable finishing flag on private data - data.finish = true; - - // Empty the queue first - jQuery.queue( this, type, [] ); - - if ( hooks && hooks.stop ) { - hooks.stop.call( this, true ); - } - - // Look for any active animations, and finish them - for ( index = timers.length; index--; ) { - if ( timers[ index ].elem === this && timers[ index ].queue === type ) { - timers[ index ].anim.stop( true ); - timers.splice( index, 1 ); - } - } - - // Look for any animations in the old queue and finish them - for ( index = 0; index < length; index++ ) { - if ( queue[ index ] && queue[ index ].finish ) { - queue[ index ].finish.call( this ); - } - } - - // Turn off finishing flag - delete data.finish; - } ); - } -} ); - -jQuery.each( [ "toggle", "show", "hide" ], function( _i, name ) { - var cssFn = jQuery.fn[ name ]; - jQuery.fn[ name ] = function( speed, easing, callback ) { - return speed == null || typeof speed === "boolean" ? - cssFn.apply( this, arguments ) : - this.animate( genFx( name, true ), speed, easing, callback ); - }; -} ); - -// Generate shortcuts for custom animations -jQuery.each( { - slideDown: genFx( "show" ), - slideUp: genFx( "hide" ), - slideToggle: genFx( "toggle" ), - fadeIn: { opacity: "show" }, - fadeOut: { opacity: "hide" }, - fadeToggle: { opacity: "toggle" } -}, function( name, props ) { - jQuery.fn[ name ] = function( speed, easing, callback ) { - return this.animate( props, speed, easing, callback ); - }; -} ); - -jQuery.timers = []; -jQuery.fx.tick = function() { - var timer, - i = 0, - timers = jQuery.timers; - - fxNow = Date.now(); - - for ( ; i < timers.length; i++ ) { - timer = timers[ i ]; - - // Run the timer and safely remove it when done (allowing for external removal) - if ( !timer() && timers[ i ] === timer ) { - timers.splice( i--, 1 ); - } - } - - if ( !timers.length ) { - jQuery.fx.stop(); - } - fxNow = undefined; -}; - -jQuery.fx.timer = function( timer ) { - jQuery.timers.push( timer ); - jQuery.fx.start(); -}; - -jQuery.fx.interval = 13; -jQuery.fx.start = function() { - if ( inProgress ) { - return; - } - - inProgress = true; - schedule(); -}; - -jQuery.fx.stop = function() { - inProgress = null; -}; - -jQuery.fx.speeds = { - slow: 600, - fast: 200, - - // Default speed - _default: 400 -}; - - -// Based off of the plugin by Clint Helfers, with permission. -jQuery.fn.delay = function( time, type ) { - time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; - type = type || "fx"; - - return this.queue( type, function( next, hooks ) { - var timeout = window.setTimeout( next, time ); - hooks.stop = function() { - window.clearTimeout( timeout ); - }; - } ); -}; - - -( function() { - var input = document.createElement( "input" ), - select = document.createElement( "select" ), - opt = select.appendChild( document.createElement( "option" ) ); - - input.type = "checkbox"; - - // Support: Android <=4.3 only - // Default value for a checkbox should be "on" - support.checkOn = input.value !== ""; - - // Support: IE <=11 only - // Must access selectedIndex to make default options select - support.optSelected = opt.selected; - - // Support: IE <=11 only - // An input loses its value after becoming a radio - input = document.createElement( "input" ); - input.value = "t"; - input.type = "radio"; - support.radioValue = input.value === "t"; -} )(); - - -var boolHook, - attrHandle = jQuery.expr.attrHandle; - -jQuery.fn.extend( { - attr: function( name, value ) { - return access( this, jQuery.attr, name, value, arguments.length > 1 ); - }, - - removeAttr: function( name ) { - return this.each( function() { - jQuery.removeAttr( this, name ); - } ); - } -} ); - -jQuery.extend( { - attr: function( elem, name, value ) { - var ret, hooks, - nType = elem.nodeType; - - // Don't get/set attributes on text, comment and attribute nodes - if ( nType === 3 || nType === 8 || nType === 2 ) { - return; - } - - // Fallback to prop when attributes are not supported - if ( typeof elem.getAttribute === "undefined" ) { - return jQuery.prop( elem, name, value ); - } - - // Attribute hooks are determined by the lowercase version - // Grab necessary hook if one is defined - if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { - hooks = jQuery.attrHooks[ name.toLowerCase() ] || - ( jQuery.expr.match.bool.test( name ) ? boolHook : undefined ); - } - - if ( value !== undefined ) { - if ( value === null ) { - jQuery.removeAttr( elem, name ); - return; - } - - if ( hooks && "set" in hooks && - ( ret = hooks.set( elem, value, name ) ) !== undefined ) { - return ret; - } - - elem.setAttribute( name, value + "" ); - return value; - } - - if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { - return ret; - } - - ret = jQuery.find.attr( elem, name ); - - // Non-existent attributes return null, we normalize to undefined - return ret == null ? undefined : ret; - }, - - attrHooks: { - type: { - set: function( elem, value ) { - if ( !support.radioValue && value === "radio" && - nodeName( elem, "input" ) ) { - var val = elem.value; - elem.setAttribute( "type", value ); - if ( val ) { - elem.value = val; - } - return value; - } - } - } - }, - - removeAttr: function( elem, value ) { - var name, - i = 0, - - // Attribute names can contain non-HTML whitespace characters - // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 - attrNames = value && value.match( rnothtmlwhite ); - - if ( attrNames && elem.nodeType === 1 ) { - while ( ( name = attrNames[ i++ ] ) ) { - elem.removeAttribute( name ); - } - } - } -} ); - -// Hooks for boolean attributes -boolHook = { - set: function( elem, value, name ) { - if ( value === false ) { - - // Remove boolean attributes when set to false - jQuery.removeAttr( elem, name ); - } else { - elem.setAttribute( name, name ); - } - return name; - } -}; - -jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( _i, name ) { - var getter = attrHandle[ name ] || jQuery.find.attr; - - attrHandle[ name ] = function( elem, name, isXML ) { - var ret, handle, - lowercaseName = name.toLowerCase(); - - if ( !isXML ) { - - // Avoid an infinite loop by temporarily removing this function from the getter - handle = attrHandle[ lowercaseName ]; - attrHandle[ lowercaseName ] = ret; - ret = getter( elem, name, isXML ) != null ? - lowercaseName : - null; - attrHandle[ lowercaseName ] = handle; - } - return ret; - }; -} ); - - - - -var rfocusable = /^(?:input|select|textarea|button)$/i, - rclickable = /^(?:a|area)$/i; - -jQuery.fn.extend( { - prop: function( name, value ) { - return access( this, jQuery.prop, name, value, arguments.length > 1 ); - }, - - removeProp: function( name ) { - return this.each( function() { - delete this[ jQuery.propFix[ name ] || name ]; - } ); - } -} ); - -jQuery.extend( { - prop: function( elem, name, value ) { - var ret, hooks, - nType = elem.nodeType; - - // Don't get/set properties on text, comment and attribute nodes - if ( nType === 3 || nType === 8 || nType === 2 ) { - return; - } - - if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { - - // Fix name and attach hooks - name = jQuery.propFix[ name ] || name; - hooks = jQuery.propHooks[ name ]; - } - - if ( value !== undefined ) { - if ( hooks && "set" in hooks && - ( ret = hooks.set( elem, value, name ) ) !== undefined ) { - return ret; - } - - return ( elem[ name ] = value ); - } - - if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { - return ret; - } - - return elem[ name ]; - }, - - propHooks: { - tabIndex: { - get: function( elem ) { - - // Support: IE <=9 - 11 only - // elem.tabIndex doesn't always return the - // correct value when it hasn't been explicitly set - // Use proper attribute retrieval (trac-12072) - var tabindex = jQuery.find.attr( elem, "tabindex" ); - - if ( tabindex ) { - return parseInt( tabindex, 10 ); - } - - if ( - rfocusable.test( elem.nodeName ) || - rclickable.test( elem.nodeName ) && - elem.href - ) { - return 0; - } - - return -1; - } - } - }, - - propFix: { - "for": "htmlFor", - "class": "className" - } -} ); - -// Support: IE <=11 only -// Accessing the selectedIndex property -// forces the browser to respect setting selected -// on the option -// The getter ensures a default option is selected -// when in an optgroup -// eslint rule "no-unused-expressions" is disabled for this code -// since it considers such accessions noop -if ( !support.optSelected ) { - jQuery.propHooks.selected = { - get: function( elem ) { - - /* eslint no-unused-expressions: "off" */ - - var parent = elem.parentNode; - if ( parent && parent.parentNode ) { - parent.parentNode.selectedIndex; - } - return null; - }, - set: function( elem ) { - - /* eslint no-unused-expressions: "off" */ - - var parent = elem.parentNode; - if ( parent ) { - parent.selectedIndex; - - if ( parent.parentNode ) { - parent.parentNode.selectedIndex; - } - } - } - }; -} - -jQuery.each( [ - "tabIndex", - "readOnly", - "maxLength", - "cellSpacing", - "cellPadding", - "rowSpan", - "colSpan", - "useMap", - "frameBorder", - "contentEditable" -], function() { - jQuery.propFix[ this.toLowerCase() ] = this; -} ); - - - - - // Strip and collapse whitespace according to HTML spec - // https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace - function stripAndCollapse( value ) { - var tokens = value.match( rnothtmlwhite ) || []; - return tokens.join( " " ); - } - - -function getClass( elem ) { - return elem.getAttribute && elem.getAttribute( "class" ) || ""; -} - -function classesToArray( value ) { - if ( Array.isArray( value ) ) { - return value; - } - if ( typeof value === "string" ) { - return value.match( rnothtmlwhite ) || []; - } - return []; -} - -jQuery.fn.extend( { - addClass: function( value ) { - var classNames, cur, curValue, className, i, finalValue; - - if ( isFunction( value ) ) { - return this.each( function( j ) { - jQuery( this ).addClass( value.call( this, j, getClass( this ) ) ); - } ); - } - - classNames = classesToArray( value ); - - if ( classNames.length ) { - return this.each( function() { - curValue = getClass( this ); - cur = this.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); - - if ( cur ) { - for ( i = 0; i < classNames.length; i++ ) { - className = classNames[ i ]; - if ( cur.indexOf( " " + className + " " ) < 0 ) { - cur += className + " "; - } - } - - // Only assign if different to avoid unneeded rendering. - finalValue = stripAndCollapse( cur ); - if ( curValue !== finalValue ) { - this.setAttribute( "class", finalValue ); - } - } - } ); - } - - return this; - }, - - removeClass: function( value ) { - var classNames, cur, curValue, className, i, finalValue; - - if ( isFunction( value ) ) { - return this.each( function( j ) { - jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) ); - } ); - } - - if ( !arguments.length ) { - return this.attr( "class", "" ); - } - - classNames = classesToArray( value ); - - if ( classNames.length ) { - return this.each( function() { - curValue = getClass( this ); - - // This expression is here for better compressibility (see addClass) - cur = this.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); - - if ( cur ) { - for ( i = 0; i < classNames.length; i++ ) { - className = classNames[ i ]; - - // Remove *all* instances - while ( cur.indexOf( " " + className + " " ) > -1 ) { - cur = cur.replace( " " + className + " ", " " ); - } - } - - // Only assign if different to avoid unneeded rendering. - finalValue = stripAndCollapse( cur ); - if ( curValue !== finalValue ) { - this.setAttribute( "class", finalValue ); - } - } - } ); - } - - return this; - }, - - toggleClass: function( value, stateVal ) { - var classNames, className, i, self, - type = typeof value, - isValidValue = type === "string" || Array.isArray( value ); - - if ( isFunction( value ) ) { - return this.each( function( i ) { - jQuery( this ).toggleClass( - value.call( this, i, getClass( this ), stateVal ), - stateVal - ); - } ); - } - - if ( typeof stateVal === "boolean" && isValidValue ) { - return stateVal ? this.addClass( value ) : this.removeClass( value ); - } - - classNames = classesToArray( value ); - - return this.each( function() { - if ( isValidValue ) { - - // Toggle individual class names - self = jQuery( this ); - - for ( i = 0; i < classNames.length; i++ ) { - className = classNames[ i ]; - - // Check each className given, space separated list - if ( self.hasClass( className ) ) { - self.removeClass( className ); - } else { - self.addClass( className ); - } - } - - // Toggle whole class name - } else if ( value === undefined || type === "boolean" ) { - className = getClass( this ); - if ( className ) { - - // Store className if set - dataPriv.set( this, "__className__", className ); - } - - // If the element has a class name or if we're passed `false`, - // then remove the whole classname (if there was one, the above saved it). - // Otherwise bring back whatever was previously saved (if anything), - // falling back to the empty string if nothing was stored. - if ( this.setAttribute ) { - this.setAttribute( "class", - className || value === false ? - "" : - dataPriv.get( this, "__className__" ) || "" - ); - } - } - } ); - }, - - hasClass: function( selector ) { - var className, elem, - i = 0; - - className = " " + selector + " "; - while ( ( elem = this[ i++ ] ) ) { - if ( elem.nodeType === 1 && - ( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) { - return true; - } - } - - return false; - } -} ); - - - - -var rreturn = /\r/g; - -jQuery.fn.extend( { - val: function( value ) { - var hooks, ret, valueIsFunction, - elem = this[ 0 ]; - - if ( !arguments.length ) { - if ( elem ) { - hooks = jQuery.valHooks[ elem.type ] || - jQuery.valHooks[ elem.nodeName.toLowerCase() ]; - - if ( hooks && - "get" in hooks && - ( ret = hooks.get( elem, "value" ) ) !== undefined - ) { - return ret; - } - - ret = elem.value; - - // Handle most common string cases - if ( typeof ret === "string" ) { - return ret.replace( rreturn, "" ); - } - - // Handle cases where value is null/undef or number - return ret == null ? "" : ret; - } - - return; - } - - valueIsFunction = isFunction( value ); - - return this.each( function( i ) { - var val; - - if ( this.nodeType !== 1 ) { - return; - } - - if ( valueIsFunction ) { - val = value.call( this, i, jQuery( this ).val() ); - } else { - val = value; - } - - // Treat null/undefined as ""; convert numbers to string - if ( val == null ) { - val = ""; - - } else if ( typeof val === "number" ) { - val += ""; - - } else if ( Array.isArray( val ) ) { - val = jQuery.map( val, function( value ) { - return value == null ? "" : value + ""; - } ); - } - - hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; - - // If set returns undefined, fall back to normal setting - if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) { - this.value = val; - } - } ); - } -} ); - -jQuery.extend( { - valHooks: { - option: { - get: function( elem ) { - - var val = jQuery.find.attr( elem, "value" ); - return val != null ? - val : - - // Support: IE <=10 - 11 only - // option.text throws exceptions (trac-14686, trac-14858) - // Strip and collapse whitespace - // https://html.spec.whatwg.org/#strip-and-collapse-whitespace - stripAndCollapse( jQuery.text( elem ) ); - } - }, - select: { - get: function( elem ) { - var value, option, i, - options = elem.options, - index = elem.selectedIndex, - one = elem.type === "select-one", - values = one ? null : [], - max = one ? index + 1 : options.length; - - if ( index < 0 ) { - i = max; - - } else { - i = one ? index : 0; - } - - // Loop through all the selected options - for ( ; i < max; i++ ) { - option = options[ i ]; - - // Support: IE <=9 only - // IE8-9 doesn't update selected after form reset (trac-2551) - if ( ( option.selected || i === index ) && - - // Don't return options that are disabled or in a disabled optgroup - !option.disabled && - ( !option.parentNode.disabled || - !nodeName( option.parentNode, "optgroup" ) ) ) { - - // Get the specific value for the option - value = jQuery( option ).val(); - - // We don't need an array for one selects - if ( one ) { - return value; - } - - // Multi-Selects return an array - values.push( value ); - } - } - - return values; - }, - - set: function( elem, value ) { - var optionSet, option, - options = elem.options, - values = jQuery.makeArray( value ), - i = options.length; - - while ( i-- ) { - option = options[ i ]; - - /* eslint-disable no-cond-assign */ - - if ( option.selected = - jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1 - ) { - optionSet = true; - } - - /* eslint-enable no-cond-assign */ - } - - // Force browsers to behave consistently when non-matching value is set - if ( !optionSet ) { - elem.selectedIndex = -1; - } - return values; - } - } - } -} ); - -// Radios and checkboxes getter/setter -jQuery.each( [ "radio", "checkbox" ], function() { - jQuery.valHooks[ this ] = { - set: function( elem, value ) { - if ( Array.isArray( value ) ) { - return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 ); - } - } - }; - if ( !support.checkOn ) { - jQuery.valHooks[ this ].get = function( elem ) { - return elem.getAttribute( "value" ) === null ? "on" : elem.value; - }; - } -} ); - - - - -// Return jQuery for attributes-only inclusion -var location = window.location; - -var nonce = { guid: Date.now() }; - -var rquery = ( /\?/ ); - - - -// Cross-browser xml parsing -jQuery.parseXML = function( data ) { - var xml, parserErrorElem; - if ( !data || typeof data !== "string" ) { - return null; - } - - // Support: IE 9 - 11 only - // IE throws on parseFromString with invalid input. - try { - xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" ); - } catch ( e ) {} - - parserErrorElem = xml && xml.getElementsByTagName( "parsererror" )[ 0 ]; - if ( !xml || parserErrorElem ) { - jQuery.error( "Invalid XML: " + ( - parserErrorElem ? - jQuery.map( parserErrorElem.childNodes, function( el ) { - return el.textContent; - } ).join( "\n" ) : - data - ) ); - } - return xml; -}; - - -var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, - stopPropagationCallback = function( e ) { - e.stopPropagation(); - }; - -jQuery.extend( jQuery.event, { - - trigger: function( event, data, elem, onlyHandlers ) { - - var i, cur, tmp, bubbleType, ontype, handle, special, lastElement, - eventPath = [ elem || document ], - type = hasOwn.call( event, "type" ) ? event.type : event, - namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : []; - - cur = lastElement = tmp = elem = elem || document; - - // Don't do events on text and comment nodes - if ( elem.nodeType === 3 || elem.nodeType === 8 ) { - return; - } - - // focus/blur morphs to focusin/out; ensure we're not firing them right now - if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { - return; - } - - if ( type.indexOf( "." ) > -1 ) { - - // Namespaced trigger; create a regexp to match event type in handle() - namespaces = type.split( "." ); - type = namespaces.shift(); - namespaces.sort(); - } - ontype = type.indexOf( ":" ) < 0 && "on" + type; - - // Caller can pass in a jQuery.Event object, Object, or just an event type string - event = event[ jQuery.expando ] ? - event : - new jQuery.Event( type, typeof event === "object" && event ); - - // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) - event.isTrigger = onlyHandlers ? 2 : 3; - event.namespace = namespaces.join( "." ); - event.rnamespace = event.namespace ? - new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) : - null; - - // Clean up the event in case it is being reused - event.result = undefined; - if ( !event.target ) { - event.target = elem; - } - - // Clone any incoming data and prepend the event, creating the handler arg list - data = data == null ? - [ event ] : - jQuery.makeArray( data, [ event ] ); - - // Allow special events to draw outside the lines - special = jQuery.event.special[ type ] || {}; - if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { - return; - } - - // Determine event propagation path in advance, per W3C events spec (trac-9951) - // Bubble up to document, then to window; watch for a global ownerDocument var (trac-9724) - if ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) { - - bubbleType = special.delegateType || type; - if ( !rfocusMorph.test( bubbleType + type ) ) { - cur = cur.parentNode; - } - for ( ; cur; cur = cur.parentNode ) { - eventPath.push( cur ); - tmp = cur; - } - - // Only add window if we got to document (e.g., not plain obj or detached DOM) - if ( tmp === ( elem.ownerDocument || document ) ) { - eventPath.push( tmp.defaultView || tmp.parentWindow || window ); - } - } - - // Fire handlers on the event path - i = 0; - while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { - lastElement = cur; - event.type = i > 1 ? - bubbleType : - special.bindType || type; - - // jQuery handler - handle = ( dataPriv.get( cur, "events" ) || Object.create( null ) )[ event.type ] && - dataPriv.get( cur, "handle" ); - if ( handle ) { - handle.apply( cur, data ); - } - - // Native handler - handle = ontype && cur[ ontype ]; - if ( handle && handle.apply && acceptData( cur ) ) { - event.result = handle.apply( cur, data ); - if ( event.result === false ) { - event.preventDefault(); - } - } - } - event.type = type; - - // If nobody prevented the default action, do it now - if ( !onlyHandlers && !event.isDefaultPrevented() ) { - - if ( ( !special._default || - special._default.apply( eventPath.pop(), data ) === false ) && - acceptData( elem ) ) { - - // Call a native DOM method on the target with the same name as the event. - // Don't do default actions on window, that's where global variables be (trac-6170) - if ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) { - - // Don't re-trigger an onFOO event when we call its FOO() method - tmp = elem[ ontype ]; - - if ( tmp ) { - elem[ ontype ] = null; - } - - // Prevent re-triggering of the same event, since we already bubbled it above - jQuery.event.triggered = type; - - if ( event.isPropagationStopped() ) { - lastElement.addEventListener( type, stopPropagationCallback ); - } - - elem[ type ](); - - if ( event.isPropagationStopped() ) { - lastElement.removeEventListener( type, stopPropagationCallback ); - } - - jQuery.event.triggered = undefined; - - if ( tmp ) { - elem[ ontype ] = tmp; - } - } - } - } - - return event.result; - }, - - // Piggyback on a donor event to simulate a different one - // Used only for `focus(in | out)` events - simulate: function( type, elem, event ) { - var e = jQuery.extend( - new jQuery.Event(), - event, - { - type: type, - isSimulated: true - } - ); - - jQuery.event.trigger( e, null, elem ); - } - -} ); - -jQuery.fn.extend( { - - trigger: function( type, data ) { - return this.each( function() { - jQuery.event.trigger( type, data, this ); - } ); - }, - triggerHandler: function( type, data ) { - var elem = this[ 0 ]; - if ( elem ) { - return jQuery.event.trigger( type, data, elem, true ); - } - } -} ); - - -var - rbracket = /\[\]$/, - rCRLF = /\r?\n/g, - rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i, - rsubmittable = /^(?:input|select|textarea|keygen)/i; - -function buildParams( prefix, obj, traditional, add ) { - var name; - - if ( Array.isArray( obj ) ) { - - // Serialize array item. - jQuery.each( obj, function( i, v ) { - if ( traditional || rbracket.test( prefix ) ) { - - // Treat each array item as a scalar. - add( prefix, v ); - - } else { - - // Item is non-scalar (array or object), encode its numeric index. - buildParams( - prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]", - v, - traditional, - add - ); - } - } ); - - } else if ( !traditional && toType( obj ) === "object" ) { - - // Serialize object item. - for ( name in obj ) { - buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); - } - - } else { - - // Serialize scalar item. - add( prefix, obj ); - } -} - -// Serialize an array of form elements or a set of -// key/values into a query string -jQuery.param = function( a, traditional ) { - var prefix, - s = [], - add = function( key, valueOrFunction ) { - - // If value is a function, invoke it and use its return value - var value = isFunction( valueOrFunction ) ? - valueOrFunction() : - valueOrFunction; - - s[ s.length ] = encodeURIComponent( key ) + "=" + - encodeURIComponent( value == null ? "" : value ); - }; - - if ( a == null ) { - return ""; - } - - // If an array was passed in, assume that it is an array of form elements. - if ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { - - // Serialize the form elements - jQuery.each( a, function() { - add( this.name, this.value ); - } ); - - } else { - - // If traditional, encode the "old" way (the way 1.3.2 or older - // did it), otherwise encode params recursively. - for ( prefix in a ) { - buildParams( prefix, a[ prefix ], traditional, add ); - } - } - - // Return the resulting serialization - return s.join( "&" ); -}; - -jQuery.fn.extend( { - serialize: function() { - return jQuery.param( this.serializeArray() ); - }, - serializeArray: function() { - return this.map( function() { - - // Can add propHook for "elements" to filter or add form elements - var elements = jQuery.prop( this, "elements" ); - return elements ? jQuery.makeArray( elements ) : this; - } ).filter( function() { - var type = this.type; - - // Use .is( ":disabled" ) so that fieldset[disabled] works - return this.name && !jQuery( this ).is( ":disabled" ) && - rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) && - ( this.checked || !rcheckableType.test( type ) ); - } ).map( function( _i, elem ) { - var val = jQuery( this ).val(); - - if ( val == null ) { - return null; - } - - if ( Array.isArray( val ) ) { - return jQuery.map( val, function( val ) { - return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; - } ); - } - - return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; - } ).get(); - } -} ); - - -var - r20 = /%20/g, - rhash = /#.*$/, - rantiCache = /([?&])_=[^&]*/, - rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg, - - // trac-7653, trac-8125, trac-8152: local protocol detection - rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, - rnoContent = /^(?:GET|HEAD)$/, - rprotocol = /^\/\//, - - /* Prefilters - * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) - * 2) These are called: - * - BEFORE asking for a transport - * - AFTER param serialization (s.data is a string if s.processData is true) - * 3) key is the dataType - * 4) the catchall symbol "*" can be used - * 5) execution will start with transport dataType and THEN continue down to "*" if needed - */ - prefilters = {}, - - /* Transports bindings - * 1) key is the dataType - * 2) the catchall symbol "*" can be used - * 3) selection will start with transport dataType and THEN go to "*" if needed - */ - transports = {}, - - // Avoid comment-prolog char sequence (trac-10098); must appease lint and evade compression - allTypes = "*/".concat( "*" ), - - // Anchor tag for parsing the document origin - originAnchor = document.createElement( "a" ); - -originAnchor.href = location.href; - -// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport -function addToPrefiltersOrTransports( structure ) { - - // dataTypeExpression is optional and defaults to "*" - return function( dataTypeExpression, func ) { - - if ( typeof dataTypeExpression !== "string" ) { - func = dataTypeExpression; - dataTypeExpression = "*"; - } - - var dataType, - i = 0, - dataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || []; - - if ( isFunction( func ) ) { - - // For each dataType in the dataTypeExpression - while ( ( dataType = dataTypes[ i++ ] ) ) { - - // Prepend if requested - if ( dataType[ 0 ] === "+" ) { - dataType = dataType.slice( 1 ) || "*"; - ( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func ); - - // Otherwise append - } else { - ( structure[ dataType ] = structure[ dataType ] || [] ).push( func ); - } - } - } - }; -} - -// Base inspection function for prefilters and transports -function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) { - - var inspected = {}, - seekingTransport = ( structure === transports ); - - function inspect( dataType ) { - var selected; - inspected[ dataType ] = true; - jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) { - var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR ); - if ( typeof dataTypeOrTransport === "string" && - !seekingTransport && !inspected[ dataTypeOrTransport ] ) { - - options.dataTypes.unshift( dataTypeOrTransport ); - inspect( dataTypeOrTransport ); - return false; - } else if ( seekingTransport ) { - return !( selected = dataTypeOrTransport ); - } - } ); - return selected; - } - - return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" ); -} - -// A special extend for ajax options -// that takes "flat" options (not to be deep extended) -// Fixes trac-9887 -function ajaxExtend( target, src ) { - var key, deep, - flatOptions = jQuery.ajaxSettings.flatOptions || {}; - - for ( key in src ) { - if ( src[ key ] !== undefined ) { - ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; - } - } - if ( deep ) { - jQuery.extend( true, target, deep ); - } - - return target; -} - -/* Handles responses to an ajax request: - * - finds the right dataType (mediates between content-type and expected dataType) - * - returns the corresponding response - */ -function ajaxHandleResponses( s, jqXHR, responses ) { - - var ct, type, finalDataType, firstDataType, - contents = s.contents, - dataTypes = s.dataTypes; - - // Remove auto dataType and get content-type in the process - while ( dataTypes[ 0 ] === "*" ) { - dataTypes.shift(); - if ( ct === undefined ) { - ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" ); - } - } - - // Check if we're dealing with a known content-type - if ( ct ) { - for ( type in contents ) { - if ( contents[ type ] && contents[ type ].test( ct ) ) { - dataTypes.unshift( type ); - break; - } - } - } - - // Check to see if we have a response for the expected dataType - if ( dataTypes[ 0 ] in responses ) { - finalDataType = dataTypes[ 0 ]; - } else { - - // Try convertible dataTypes - for ( type in responses ) { - if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) { - finalDataType = type; - break; - } - if ( !firstDataType ) { - firstDataType = type; - } - } - - // Or just use first one - finalDataType = finalDataType || firstDataType; - } - - // If we found a dataType - // We add the dataType to the list if needed - // and return the corresponding response - if ( finalDataType ) { - if ( finalDataType !== dataTypes[ 0 ] ) { - dataTypes.unshift( finalDataType ); - } - return responses[ finalDataType ]; - } -} - -/* Chain conversions given the request and the original response - * Also sets the responseXXX fields on the jqXHR instance - */ -function ajaxConvert( s, response, jqXHR, isSuccess ) { - var conv2, current, conv, tmp, prev, - converters = {}, - - // Work with a copy of dataTypes in case we need to modify it for conversion - dataTypes = s.dataTypes.slice(); - - // Create converters map with lowercased keys - if ( dataTypes[ 1 ] ) { - for ( conv in s.converters ) { - converters[ conv.toLowerCase() ] = s.converters[ conv ]; - } - } - - current = dataTypes.shift(); - - // Convert to each sequential dataType - while ( current ) { - - if ( s.responseFields[ current ] ) { - jqXHR[ s.responseFields[ current ] ] = response; - } - - // Apply the dataFilter if provided - if ( !prev && isSuccess && s.dataFilter ) { - response = s.dataFilter( response, s.dataType ); - } - - prev = current; - current = dataTypes.shift(); - - if ( current ) { - - // There's only work to do if current dataType is non-auto - if ( current === "*" ) { - - current = prev; - - // Convert response if prev dataType is non-auto and differs from current - } else if ( prev !== "*" && prev !== current ) { - - // Seek a direct converter - conv = converters[ prev + " " + current ] || converters[ "* " + current ]; - - // If none found, seek a pair - if ( !conv ) { - for ( conv2 in converters ) { - - // If conv2 outputs current - tmp = conv2.split( " " ); - if ( tmp[ 1 ] === current ) { - - // If prev can be converted to accepted input - conv = converters[ prev + " " + tmp[ 0 ] ] || - converters[ "* " + tmp[ 0 ] ]; - if ( conv ) { - - // Condense equivalence converters - if ( conv === true ) { - conv = converters[ conv2 ]; - - // Otherwise, insert the intermediate dataType - } else if ( converters[ conv2 ] !== true ) { - current = tmp[ 0 ]; - dataTypes.unshift( tmp[ 1 ] ); - } - break; - } - } - } - } - - // Apply converter (if not an equivalence) - if ( conv !== true ) { - - // Unless errors are allowed to bubble, catch and return them - if ( conv && s.throws ) { - response = conv( response ); - } else { - try { - response = conv( response ); - } catch ( e ) { - return { - state: "parsererror", - error: conv ? e : "No conversion from " + prev + " to " + current - }; - } - } - } - } - } - } - - return { state: "success", data: response }; -} - -jQuery.extend( { - - // Counter for holding the number of active queries - active: 0, - - // Last-Modified header cache for next request - lastModified: {}, - etag: {}, - - ajaxSettings: { - url: location.href, - type: "GET", - isLocal: rlocalProtocol.test( location.protocol ), - global: true, - processData: true, - async: true, - contentType: "application/x-www-form-urlencoded; charset=UTF-8", - - /* - timeout: 0, - data: null, - dataType: null, - username: null, - password: null, - cache: null, - throws: false, - traditional: false, - headers: {}, - */ - - accepts: { - "*": allTypes, - text: "text/plain", - html: "text/html", - xml: "application/xml, text/xml", - json: "application/json, text/javascript" - }, - - contents: { - xml: /\bxml\b/, - html: /\bhtml/, - json: /\bjson\b/ - }, - - responseFields: { - xml: "responseXML", - text: "responseText", - json: "responseJSON" - }, - - // Data converters - // Keys separate source (or catchall "*") and destination types with a single space - converters: { - - // Convert anything to text - "* text": String, - - // Text to html (true = no transformation) - "text html": true, - - // Evaluate text as a json expression - "text json": JSON.parse, - - // Parse text as xml - "text xml": jQuery.parseXML - }, - - // For options that shouldn't be deep extended: - // you can add your own custom options here if - // and when you create one that shouldn't be - // deep extended (see ajaxExtend) - flatOptions: { - url: true, - context: true - } - }, - - // Creates a full fledged settings object into target - // with both ajaxSettings and settings fields. - // If target is omitted, writes into ajaxSettings. - ajaxSetup: function( target, settings ) { - return settings ? - - // Building a settings object - ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) : - - // Extending ajaxSettings - ajaxExtend( jQuery.ajaxSettings, target ); - }, - - ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), - ajaxTransport: addToPrefiltersOrTransports( transports ), - - // Main method - ajax: function( url, options ) { - - // If url is an object, simulate pre-1.5 signature - if ( typeof url === "object" ) { - options = url; - url = undefined; - } - - // Force options to be an object - options = options || {}; - - var transport, - - // URL without anti-cache param - cacheURL, - - // Response headers - responseHeadersString, - responseHeaders, - - // timeout handle - timeoutTimer, - - // Url cleanup var - urlAnchor, - - // Request state (becomes false upon send and true upon completion) - completed, - - // To know if global events are to be dispatched - fireGlobals, - - // Loop variable - i, - - // uncached part of the url - uncached, - - // Create the final options object - s = jQuery.ajaxSetup( {}, options ), - - // Callbacks context - callbackContext = s.context || s, - - // Context for global events is callbackContext if it is a DOM node or jQuery collection - globalEventContext = s.context && - ( callbackContext.nodeType || callbackContext.jquery ) ? - jQuery( callbackContext ) : - jQuery.event, - - // Deferreds - deferred = jQuery.Deferred(), - completeDeferred = jQuery.Callbacks( "once memory" ), - - // Status-dependent callbacks - statusCode = s.statusCode || {}, - - // Headers (they are sent all at once) - requestHeaders = {}, - requestHeadersNames = {}, - - // Default abort message - strAbort = "canceled", - - // Fake xhr - jqXHR = { - readyState: 0, - - // Builds headers hashtable if needed - getResponseHeader: function( key ) { - var match; - if ( completed ) { - if ( !responseHeaders ) { - responseHeaders = {}; - while ( ( match = rheaders.exec( responseHeadersString ) ) ) { - responseHeaders[ match[ 1 ].toLowerCase() + " " ] = - ( responseHeaders[ match[ 1 ].toLowerCase() + " " ] || [] ) - .concat( match[ 2 ] ); - } - } - match = responseHeaders[ key.toLowerCase() + " " ]; - } - return match == null ? null : match.join( ", " ); - }, - - // Raw string - getAllResponseHeaders: function() { - return completed ? responseHeadersString : null; - }, - - // Caches the header - setRequestHeader: function( name, value ) { - if ( completed == null ) { - name = requestHeadersNames[ name.toLowerCase() ] = - requestHeadersNames[ name.toLowerCase() ] || name; - requestHeaders[ name ] = value; - } - return this; - }, - - // Overrides response content-type header - overrideMimeType: function( type ) { - if ( completed == null ) { - s.mimeType = type; - } - return this; - }, - - // Status-dependent callbacks - statusCode: function( map ) { - var code; - if ( map ) { - if ( completed ) { - - // Execute the appropriate callbacks - jqXHR.always( map[ jqXHR.status ] ); - } else { - - // Lazy-add the new callbacks in a way that preserves old ones - for ( code in map ) { - statusCode[ code ] = [ statusCode[ code ], map[ code ] ]; - } - } - } - return this; - }, - - // Cancel the request - abort: function( statusText ) { - var finalText = statusText || strAbort; - if ( transport ) { - transport.abort( finalText ); - } - done( 0, finalText ); - return this; - } - }; - - // Attach deferreds - deferred.promise( jqXHR ); - - // Add protocol if not provided (prefilters might expect it) - // Handle falsy url in the settings object (trac-10093: consistency with old signature) - // We also use the url parameter if available - s.url = ( ( url || s.url || location.href ) + "" ) - .replace( rprotocol, location.protocol + "//" ); - - // Alias method option to type as per ticket trac-12004 - s.type = options.method || options.type || s.method || s.type; - - // Extract dataTypes list - s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnothtmlwhite ) || [ "" ]; - - // A cross-domain request is in order when the origin doesn't match the current origin. - if ( s.crossDomain == null ) { - urlAnchor = document.createElement( "a" ); - - // Support: IE <=8 - 11, Edge 12 - 15 - // IE throws exception on accessing the href property if url is malformed, - // e.g. http://example.com:80x/ - try { - urlAnchor.href = s.url; - - // Support: IE <=8 - 11 only - // Anchor's host property isn't correctly set when s.url is relative - urlAnchor.href = urlAnchor.href; - s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !== - urlAnchor.protocol + "//" + urlAnchor.host; - } catch ( e ) { - - // If there is an error parsing the URL, assume it is crossDomain, - // it can be rejected by the transport if it is invalid - s.crossDomain = true; - } - } - - // Convert data if not already a string - if ( s.data && s.processData && typeof s.data !== "string" ) { - s.data = jQuery.param( s.data, s.traditional ); - } - - // Apply prefilters - inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); - - // If request was aborted inside a prefilter, stop there - if ( completed ) { - return jqXHR; - } - - // We can fire global events as of now if asked to - // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (trac-15118) - fireGlobals = jQuery.event && s.global; - - // Watch for a new set of requests - if ( fireGlobals && jQuery.active++ === 0 ) { - jQuery.event.trigger( "ajaxStart" ); - } - - // Uppercase the type - s.type = s.type.toUpperCase(); - - // Determine if request has content - s.hasContent = !rnoContent.test( s.type ); - - // Save the URL in case we're toying with the If-Modified-Since - // and/or If-None-Match header later on - // Remove hash to simplify url manipulation - cacheURL = s.url.replace( rhash, "" ); - - // More options handling for requests with no content - if ( !s.hasContent ) { - - // Remember the hash so we can put it back - uncached = s.url.slice( cacheURL.length ); - - // If data is available and should be processed, append data to url - if ( s.data && ( s.processData || typeof s.data === "string" ) ) { - cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data; - - // trac-9682: remove data so that it's not used in an eventual retry - delete s.data; - } - - // Add or update anti-cache param if needed - if ( s.cache === false ) { - cacheURL = cacheURL.replace( rantiCache, "$1" ); - uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce.guid++ ) + - uncached; - } - - // Put hash and anti-cache on the URL that will be requested (gh-1732) - s.url = cacheURL + uncached; - - // Change '%20' to '+' if this is encoded form body content (gh-2658) - } else if ( s.data && s.processData && - ( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) { - s.data = s.data.replace( r20, "+" ); - } - - // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. - if ( s.ifModified ) { - if ( jQuery.lastModified[ cacheURL ] ) { - jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] ); - } - if ( jQuery.etag[ cacheURL ] ) { - jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] ); - } - } - - // Set the correct header, if data is being sent - if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { - jqXHR.setRequestHeader( "Content-Type", s.contentType ); - } - - // Set the Accepts header for the server, depending on the dataType - jqXHR.setRequestHeader( - "Accept", - s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ? - s.accepts[ s.dataTypes[ 0 ] ] + - ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : - s.accepts[ "*" ] - ); - - // Check for headers option - for ( i in s.headers ) { - jqXHR.setRequestHeader( i, s.headers[ i ] ); - } - - // Allow custom headers/mimetypes and early abort - if ( s.beforeSend && - ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) { - - // Abort if not done already and return - return jqXHR.abort(); - } - - // Aborting is no longer a cancellation - strAbort = "abort"; - - // Install callbacks on deferreds - completeDeferred.add( s.complete ); - jqXHR.done( s.success ); - jqXHR.fail( s.error ); - - // Get transport - transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); - - // If no transport, we auto-abort - if ( !transport ) { - done( -1, "No Transport" ); - } else { - jqXHR.readyState = 1; - - // Send global event - if ( fireGlobals ) { - globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); - } - - // If request was aborted inside ajaxSend, stop there - if ( completed ) { - return jqXHR; - } - - // Timeout - if ( s.async && s.timeout > 0 ) { - timeoutTimer = window.setTimeout( function() { - jqXHR.abort( "timeout" ); - }, s.timeout ); - } - - try { - completed = false; - transport.send( requestHeaders, done ); - } catch ( e ) { - - // Rethrow post-completion exceptions - if ( completed ) { - throw e; - } - - // Propagate others as results - done( -1, e ); - } - } - - // Callback for when everything is done - function done( status, nativeStatusText, responses, headers ) { - var isSuccess, success, error, response, modified, - statusText = nativeStatusText; - - // Ignore repeat invocations - if ( completed ) { - return; - } - - completed = true; - - // Clear timeout if it exists - if ( timeoutTimer ) { - window.clearTimeout( timeoutTimer ); - } - - // Dereference transport for early garbage collection - // (no matter how long the jqXHR object will be used) - transport = undefined; - - // Cache response headers - responseHeadersString = headers || ""; - - // Set readyState - jqXHR.readyState = status > 0 ? 4 : 0; - - // Determine if successful - isSuccess = status >= 200 && status < 300 || status === 304; - - // Get response data - if ( responses ) { - response = ajaxHandleResponses( s, jqXHR, responses ); - } - - // Use a noop converter for missing script but not if jsonp - if ( !isSuccess && - jQuery.inArray( "script", s.dataTypes ) > -1 && - jQuery.inArray( "json", s.dataTypes ) < 0 ) { - s.converters[ "text script" ] = function() {}; - } - - // Convert no matter what (that way responseXXX fields are always set) - response = ajaxConvert( s, response, jqXHR, isSuccess ); - - // If successful, handle type chaining - if ( isSuccess ) { - - // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. - if ( s.ifModified ) { - modified = jqXHR.getResponseHeader( "Last-Modified" ); - if ( modified ) { - jQuery.lastModified[ cacheURL ] = modified; - } - modified = jqXHR.getResponseHeader( "etag" ); - if ( modified ) { - jQuery.etag[ cacheURL ] = modified; - } - } - - // if no content - if ( status === 204 || s.type === "HEAD" ) { - statusText = "nocontent"; - - // if not modified - } else if ( status === 304 ) { - statusText = "notmodified"; - - // If we have data, let's convert it - } else { - statusText = response.state; - success = response.data; - error = response.error; - isSuccess = !error; - } - } else { - - // Extract error from statusText and normalize for non-aborts - error = statusText; - if ( status || !statusText ) { - statusText = "error"; - if ( status < 0 ) { - status = 0; - } - } - } - - // Set data for the fake xhr object - jqXHR.status = status; - jqXHR.statusText = ( nativeStatusText || statusText ) + ""; - - // Success/Error - if ( isSuccess ) { - deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); - } else { - deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); - } - - // Status-dependent callbacks - jqXHR.statusCode( statusCode ); - statusCode = undefined; - - if ( fireGlobals ) { - globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError", - [ jqXHR, s, isSuccess ? success : error ] ); - } - - // Complete - completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); - - if ( fireGlobals ) { - globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); - - // Handle the global AJAX counter - if ( !( --jQuery.active ) ) { - jQuery.event.trigger( "ajaxStop" ); - } - } - } - - return jqXHR; - }, - - getJSON: function( url, data, callback ) { - return jQuery.get( url, data, callback, "json" ); - }, - - getScript: function( url, callback ) { - return jQuery.get( url, undefined, callback, "script" ); - } -} ); - -jQuery.each( [ "get", "post" ], function( _i, method ) { - jQuery[ method ] = function( url, data, callback, type ) { - - // Shift arguments if data argument was omitted - if ( isFunction( data ) ) { - type = type || callback; - callback = data; - data = undefined; - } - - // The url can be an options object (which then must have .url) - return jQuery.ajax( jQuery.extend( { - url: url, - type: method, - dataType: type, - data: data, - success: callback - }, jQuery.isPlainObject( url ) && url ) ); - }; -} ); - -jQuery.ajaxPrefilter( function( s ) { - var i; - for ( i in s.headers ) { - if ( i.toLowerCase() === "content-type" ) { - s.contentType = s.headers[ i ] || ""; - } - } -} ); - - -jQuery._evalUrl = function( url, options, doc ) { - return jQuery.ajax( { - url: url, - - // Make this explicit, since user can override this through ajaxSetup (trac-11264) - type: "GET", - dataType: "script", - cache: true, - async: false, - global: false, - - // Only evaluate the response if it is successful (gh-4126) - // dataFilter is not invoked for failure responses, so using it instead - // of the default converter is kludgy but it works. - converters: { - "text script": function() {} - }, - dataFilter: function( response ) { - jQuery.globalEval( response, options, doc ); - } - } ); -}; - - -jQuery.fn.extend( { - wrapAll: function( html ) { - var wrap; - - if ( this[ 0 ] ) { - if ( isFunction( html ) ) { - html = html.call( this[ 0 ] ); - } - - // The elements to wrap the target around - wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true ); - - if ( this[ 0 ].parentNode ) { - wrap.insertBefore( this[ 0 ] ); - } - - wrap.map( function() { - var elem = this; - - while ( elem.firstElementChild ) { - elem = elem.firstElementChild; - } - - return elem; - } ).append( this ); - } - - return this; - }, - - wrapInner: function( html ) { - if ( isFunction( html ) ) { - return this.each( function( i ) { - jQuery( this ).wrapInner( html.call( this, i ) ); - } ); - } - - return this.each( function() { - var self = jQuery( this ), - contents = self.contents(); - - if ( contents.length ) { - contents.wrapAll( html ); - - } else { - self.append( html ); - } - } ); - }, - - wrap: function( html ) { - var htmlIsFunction = isFunction( html ); - - return this.each( function( i ) { - jQuery( this ).wrapAll( htmlIsFunction ? html.call( this, i ) : html ); - } ); - }, - - unwrap: function( selector ) { - this.parent( selector ).not( "body" ).each( function() { - jQuery( this ).replaceWith( this.childNodes ); - } ); - return this; - } -} ); - - -jQuery.expr.pseudos.hidden = function( elem ) { - return !jQuery.expr.pseudos.visible( elem ); -}; -jQuery.expr.pseudos.visible = function( elem ) { - return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ); -}; - - - - -jQuery.ajaxSettings.xhr = function() { - try { - return new window.XMLHttpRequest(); - } catch ( e ) {} -}; - -var xhrSuccessStatus = { - - // File protocol always yields status code 0, assume 200 - 0: 200, - - // Support: IE <=9 only - // trac-1450: sometimes IE returns 1223 when it should be 204 - 1223: 204 - }, - xhrSupported = jQuery.ajaxSettings.xhr(); - -support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported ); -support.ajax = xhrSupported = !!xhrSupported; - -jQuery.ajaxTransport( function( options ) { - var callback, errorCallback; - - // Cross domain only allowed if supported through XMLHttpRequest - if ( support.cors || xhrSupported && !options.crossDomain ) { - return { - send: function( headers, complete ) { - var i, - xhr = options.xhr(); - - xhr.open( - options.type, - options.url, - options.async, - options.username, - options.password - ); - - // Apply custom fields if provided - if ( options.xhrFields ) { - for ( i in options.xhrFields ) { - xhr[ i ] = options.xhrFields[ i ]; - } - } - - // Override mime type if needed - if ( options.mimeType && xhr.overrideMimeType ) { - xhr.overrideMimeType( options.mimeType ); - } - - // X-Requested-With header - // For cross-domain requests, seeing as conditions for a preflight are - // akin to a jigsaw puzzle, we simply never set it to be sure. - // (it can always be set on a per-request basis or even using ajaxSetup) - // For same-domain requests, won't change header if already provided. - if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) { - headers[ "X-Requested-With" ] = "XMLHttpRequest"; - } - - // Set headers - for ( i in headers ) { - xhr.setRequestHeader( i, headers[ i ] ); - } - - // Callback - callback = function( type ) { - return function() { - if ( callback ) { - callback = errorCallback = xhr.onload = - xhr.onerror = xhr.onabort = xhr.ontimeout = - xhr.onreadystatechange = null; - - if ( type === "abort" ) { - xhr.abort(); - } else if ( type === "error" ) { - - // Support: IE <=9 only - // On a manual native abort, IE9 throws - // errors on any property access that is not readyState - if ( typeof xhr.status !== "number" ) { - complete( 0, "error" ); - } else { - complete( - - // File: protocol always yields status 0; see trac-8605, trac-14207 - xhr.status, - xhr.statusText - ); - } - } else { - complete( - xhrSuccessStatus[ xhr.status ] || xhr.status, - xhr.statusText, - - // Support: IE <=9 only - // IE9 has no XHR2 but throws on binary (trac-11426) - // For XHR2 non-text, let the caller handle it (gh-2498) - ( xhr.responseType || "text" ) !== "text" || - typeof xhr.responseText !== "string" ? - { binary: xhr.response } : - { text: xhr.responseText }, - xhr.getAllResponseHeaders() - ); - } - } - }; - }; - - // Listen to events - xhr.onload = callback(); - errorCallback = xhr.onerror = xhr.ontimeout = callback( "error" ); - - // Support: IE 9 only - // Use onreadystatechange to replace onabort - // to handle uncaught aborts - if ( xhr.onabort !== undefined ) { - xhr.onabort = errorCallback; - } else { - xhr.onreadystatechange = function() { - - // Check readyState before timeout as it changes - if ( xhr.readyState === 4 ) { - - // Allow onerror to be called first, - // but that will not handle a native abort - // Also, save errorCallback to a variable - // as xhr.onerror cannot be accessed - window.setTimeout( function() { - if ( callback ) { - errorCallback(); - } - } ); - } - }; - } - - // Create the abort callback - callback = callback( "abort" ); - - try { - - // Do send the request (this may raise an exception) - xhr.send( options.hasContent && options.data || null ); - } catch ( e ) { - - // trac-14683: Only rethrow if this hasn't been notified as an error yet - if ( callback ) { - throw e; - } - } - }, - - abort: function() { - if ( callback ) { - callback(); - } - } - }; - } -} ); - - - - -// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432) -jQuery.ajaxPrefilter( function( s ) { - if ( s.crossDomain ) { - s.contents.script = false; - } -} ); - -// Install script dataType -jQuery.ajaxSetup( { - accepts: { - script: "text/javascript, application/javascript, " + - "application/ecmascript, application/x-ecmascript" - }, - contents: { - script: /\b(?:java|ecma)script\b/ - }, - converters: { - "text script": function( text ) { - jQuery.globalEval( text ); - return text; - } - } -} ); - -// Handle cache's special case and crossDomain -jQuery.ajaxPrefilter( "script", function( s ) { - if ( s.cache === undefined ) { - s.cache = false; - } - if ( s.crossDomain ) { - s.type = "GET"; - } -} ); - -// Bind script tag hack transport -jQuery.ajaxTransport( "script", function( s ) { - - // This transport only deals with cross domain or forced-by-attrs requests - if ( s.crossDomain || s.scriptAttrs ) { - var script, callback; - return { - send: function( _, complete ) { - script = jQuery( " - """, - """ - """, """ const pathtoroot = "./"; loadScripts(); @@ -437,6 +429,10 @@ public class TestSearch extends JavadocTester { holder="Search documentation (type /)" aria-label="Search in documentation" auto\ complete="off" spellcheck="false">"""); + checkOutput(fileName, false, + "jquery-ui.min.css", + "jquery-3.7.1.min.js", + "jquery-ui.min.js"); } void checkSingleIndex() { @@ -669,14 +665,15 @@ public class TestSearch extends JavadocTester { "AnotherClass.java:68: warning: invalid usage of tag {@index"); } - void checkJqueryAndImageFiles(boolean expectedOutput) { + void checkImageFiles(boolean expectedOutput) { checkFiles(expectedOutput, "script-files/search.js", - "script-files/jquery-3.7.1.min.js", - "script-files/jquery-ui.min.js", - "resource-files/jquery-ui.min.css", "resource-files/x.svg", "resource-files/glass.svg"); + checkFiles(false, + "script-files/jquery-3.7.1.min.js", + "script-files/jquery-ui.min.js", + "resource-files/jquery-ui.min.css"); } void checkSearchJS() { @@ -689,9 +686,7 @@ public class TestSearch extends JavadocTester { "function getURLPrefix(item, category) {", "url += item.l;"); - checkOutput("script-files/search-page.js", true, - "function renderResults(result) {", - "function selectTab(category) {"); + checkFiles(false, "script-files/search-page.js"); checkCssClasses("script-files/search.js", "resource-files/stylesheet.css"); } @@ -701,8 +696,8 @@ public class TestSearch extends JavadocTester { // are also defined as class selectors somewhere in the stylesheet file. String js = readOutputFile(jsFile); Set cssClasses = new TreeSet<>(); - addMatches(js, Pattern.compile("class=\\\\*\"([^\\\\\"]+)\\\\*\""), cssClasses); - addMatches(js, Pattern.compile("attr\\(\"class\", \"([^\"]+)\"\\)"), cssClasses); + addMatches(js, Pattern.compile("class=[\"']([-\\w]+)[\"']"), cssClasses); + addMatches(js, Pattern.compile("classList.add\\([\"']([-\\w]+)[\"']\\)"), cssClasses); // verify that the regex did find use of CSS class names checking("Checking CSS classes found"); if (cssClasses.isEmpty()) { diff --git a/test/langtools/jdk/javadoc/doclet/testSerializedFormWithClassFile/TestSerializedFormWithClassFile.java b/test/langtools/jdk/javadoc/doclet/testSerializedFormWithClassFile/TestSerializedFormWithClassFile.java index 25613dffd95..1db0b6af8d4 100644 --- a/test/langtools/jdk/javadoc/doclet/testSerializedFormWithClassFile/TestSerializedFormWithClassFile.java +++ b/test/langtools/jdk/javadoc/doclet/testSerializedFormWithClassFile/TestSerializedFormWithClassFile.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -106,8 +106,8 @@ public class TestSerializedFormWithClassFile extends JavadocTester { new JavacTask(tb).files(srcDir.resolve("A.java")).outdir(classes).run(); new ClassBuilder(tb, "B") - .setExtends("A") .setModifiers("public", "class") + .setExtends("A") .write(srcDir); } } diff --git a/test/langtools/jdk/javadoc/doclet/testStylesheet/TestStylesheet.java b/test/langtools/jdk/javadoc/doclet/testStylesheet/TestStylesheet.java index 012e9ce00de..a2c2a603212 100644 --- a/test/langtools/jdk/javadoc/doclet/testStylesheet/TestStylesheet.java +++ b/test/langtools/jdk/javadoc/doclet/testStylesheet/TestStylesheet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, 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 @@ -132,6 +132,7 @@ public class TestStylesheet extends JavadocTester { min-height:12px; font-size:0; visibility:hidden; + cursor: pointer; }""", """ ::placeholder { diff --git a/test/langtools/jdk/javadoc/doclet/testVisibleMembers/TestVisibleMembers.java b/test/langtools/jdk/javadoc/doclet/testVisibleMembers/TestVisibleMembers.java index 09f6f92e2b6..fe5f1212b09 100644 --- a/test/langtools/jdk/javadoc/doclet/testVisibleMembers/TestVisibleMembers.java +++ b/test/langtools/jdk/javadoc/doclet/testVisibleMembers/TestVisibleMembers.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2026, 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 @@ -135,7 +135,7 @@ public class TestVisibleMembers extends JavadocTester { "@param lvalue an lvalue", "@return something"); new ClassBuilder(tb, "p.B") - .setModifiers( "public", "interface") + .setModifiers("public", "interface") .setExtends("A") .addMembers(mbWith1, mbWith2) .write(srcDir); @@ -358,7 +358,7 @@ public class TestVisibleMembers extends JavadocTester { MethodBuilder.parse("public I sub() {return null;}"), MethodBuilder.parse("public I sub1() {return null;}") .setComments(Kind.INHERIT_DOC), - MethodBuilder.parse(" public void method() {}") + MethodBuilder.parse("public void method() {}") .setComments("A method ", "@see #sub", "@see #sub1"), MethodBuilder.parse("public int length(){return 1;}") .setComments(Kind.NO_API_COMMENT) @@ -380,7 +380,7 @@ public class TestVisibleMembers extends JavadocTester { ).write(srcDir); new ClassBuilder(tb, "p.QLong") - .setModifiers("public interface") + .setModifiers("public", "interface") .addMembers( MethodBuilder.parse("default void forEach(Q action) {}") ).write(srcDir); @@ -663,7 +663,7 @@ public class TestVisibleMembers extends JavadocTester { ).write(srcDir); new ClassBuilder(tb, "p.I3") - .setExtends("I1, I2") + .addImplements("I1", "I2") .setModifiers("public", "interface") .addMembers( FieldBuilder.parse("public static int field = 3;"), @@ -677,8 +677,8 @@ public class TestVisibleMembers extends JavadocTester { .write(srcDir); new ClassBuilder(tb, "p.C2") - .setExtends("C1") .setModifiers("public", "abstract", "class") + .setExtends("C1") .addMembers( FieldBuilder.parse("public int field;"), MethodBuilder.parse("public void method(){}"), diff --git a/test/langtools/jdk/javadoc/taglet/JdkTaglets.java b/test/langtools/jdk/javadoc/taglet/JdkTaglets.java new file mode 100644 index 00000000000..537904450aa --- /dev/null +++ b/test/langtools/jdk/javadoc/taglet/JdkTaglets.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2026, 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. + */ + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.stream.Stream; + +import toolbox.JavacTask; +import toolbox.ToolBox; + +/// Utilities to build the JDK-specific taglets. +/// This guy uses JavacTask so can't be in javadoc.tester. +public final class JdkTaglets { + + /// Build a taglet and return its path for `-tagletpath`. + public static Path build(ToolBox tb, Path base, String... tagletFiles) throws IOException { + Path tagletOutDir = base.resolve("tagletClasses"); + Files.createDirectories(tagletOutDir); + tb.cleanDirectory(tagletOutDir); + Path tagletRoot = tb.findFromTestRoot("../../make/jdk/src/classes/build/tools/taglet"); + + new JavacTask(tb) + .files(Stream.of(tagletFiles) + .map(tagletFile -> tagletRoot.resolve(tagletFile + ".java")) + .toArray(Path[]::new)) + .outdir(tagletOutDir) + .run(JavacTask.Expect.SUCCESS); + return tagletOutDir; + } + + private JdkTaglets() {} +} diff --git a/test/langtools/jdk/javadoc/taglet/sealedGraph/TestSealedTaglet.java b/test/langtools/jdk/javadoc/taglet/sealedGraph/TestSealedTaglet.java new file mode 100644 index 00000000000..3ac6f0601b2 --- /dev/null +++ b/test/langtools/jdk/javadoc/taglet/sealedGraph/TestSealedTaglet.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2026, 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. + */ + +/* + * @test + * @summary General tests for SealedGraph block tag + * @bug 8380913 + * @library /tools/lib /jdk/javadoc/lib ../ + * @modules jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * jdk.javadoc/jdk.javadoc.internal.tool + * @build javadoc.tester.* toolbox.ToolBox builder.ClassBuilder JdkTaglets + * @run main ${test.main.class} + */ + +import java.nio.file.Path; + +import builder.ClassBuilder; +import javadoc.tester.JavadocTester; +import toolbox.ToolBox; + +public class TestSealedTaglet extends JavadocTester { + + final ToolBox tb; + final Path tagletPath; + + public static void main(String... args) throws Exception { + var tester = new TestSealedTaglet(); + tester.runTests(); + } + + TestSealedTaglet() throws Exception { + tb = new ToolBox(); + tagletPath = JdkTaglets.build(tb, Path.of(""), "SealedGraph"); + setAutomaticCheckLinks(false); // Don't fail for missing svg + } + + @Test + public void testInvisibleInMiddle(Path base) throws Exception { + Path srcDir = base.resolve("src"); + Path outDir = base.resolve("out"); + + tb.writeFile(srcDir.resolve("module-info.java"), + """ + module test { + exports pkg; + } + """); + new ClassBuilder(tb, "pkg.A") + .setModifiers("public", "abstract", "sealed", "interface") + .setComments("@sealedGraph") + .addPermits("pkg.B") + .write(srcDir); + new ClassBuilder(tb, "pkg.B") + .setModifiers("abstract", "sealed", "interface") + .addImplements("pkg.A") + .addPermits("pkg.C", "pkg.D") + .write(srcDir); + new ClassBuilder(tb, "pkg.C") + .setModifiers("abstract", "sealed", "interface") + .addImplements("pkg.A", "pkg.B") + .addPermits("pkg.D") + .write(srcDir); + new ClassBuilder(tb, "pkg.D") + .setModifiers("public", "final", "class") + .addImplements("pkg.B", "pkg.C") + .write(srcDir); + + System.setProperty("sealedDotOutputDir", outDir.toString()); + + javadoc("-tagletpath", tagletPath.toString(), + "-taglet", "build.tools.taglet.SealedGraph", + "-d", outDir.toString(), + "-sourcepath", srcDir.toString(), + "pkg"); + + checkExit(Exit.OK); + // D is displayed as a direct subtype of A, bypassing B, C, one link only + checkUnique("test_pkg.A.dot", "\"pkg.D\" -> \"pkg.A\";"); + } +} diff --git a/test/langtools/jdk/javadoc/tool/api/basic/APITest.java b/test/langtools/jdk/javadoc/tool/api/basic/APITest.java index 71908f34e99..bd8d0cf7953 100644 --- a/test/langtools/jdk/javadoc/tool/api/basic/APITest.java +++ b/test/langtools/jdk/javadoc/tool/api/basic/APITest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, 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 @@ -207,7 +207,6 @@ class APITest { "resource-files/copy.svg", "resource-files/down.svg", "resource-files/glass.svg", - "resource-files/jquery-ui.min.css", "resource-files/left.svg", "resource-files/link.svg", "resource-files/moon.svg", @@ -241,11 +240,8 @@ class APITest { "resource-files/fonts/DejaVuLGCSerif-Italic.woff2", "resource-files/fonts/DejaVuLGCSerif.woff", "resource-files/fonts/DejaVuLGCSerif.woff2", - "script-files/jquery-3.7.1.min.js", - "script-files/jquery-ui.min.js", "script-files/script.js", "script-files/search.js", - "script-files/search-page.js", "tag-search-index.js", "type-search-index.js" )); @@ -255,11 +251,8 @@ class APITest { !s.endsWith("-search-index.js") && !s.equals("index-all.html") && !s.equals("resource-files/glass.svg") - && !s.equals("resource-files/jquery-ui.min.css") && !s.equals("resource-files/x.svg") - && !s.startsWith("script-files/jquery-") && !s.equals("script-files/search.js") - && !s.equals("script-files/search-page.js") && !s.equals("search.html") && !s.equals("allclasses-index.html") && !s.equals("allpackages-index.html") diff --git a/test/langtools/tools/javac/TextBlockU2028.java b/test/langtools/tools/javac/TextBlockU2028.java new file mode 100644 index 00000000000..a7abfe43263 --- /dev/null +++ b/test/langtools/tools/javac/TextBlockU2028.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2026, 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. + */ + +/* + * @test + * @bug 8380912 + * @summary Verify that trailing whitespace warning is not reported for \u2028 + * inside text block content + * @library /tools/lib + * @modules + * jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * @build toolbox.ToolBox toolbox.JavacTask + * @run junit TextBlockU2028 + */ + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import toolbox.JavacTask; +import toolbox.ToolBox; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; + +public class TextBlockU2028 { + Path base; + ToolBox tb = new ToolBox(); + + @Test + void testNoFalseTrailingWhitespaceWarning() throws Exception { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + new JavacTask(tb) + .options("-d", classes.toString(), "-Xlint:text-blocks", "-XDrawDiagnostics", "-Werror") + .sources(""" + public class Test { + String s = \"\"\" + foo \\u2028 bar + \"\"\"; + } + """) + .run() + .writeAll(); + } + + @BeforeEach + public void setUp(TestInfo info) { + base = Paths.get(".") + .resolve(info.getTestMethod() + .orElseThrow() + .getName()); + } +} diff --git a/test/langtools/tools/javac/annotations/8218152/MalformedAnnotationProcessorTests.java b/test/langtools/tools/javac/annotations/8218152/MalformedAnnotationProcessorTests.java index 68e4aea0ab2..85095cc5537 100644 --- a/test/langtools/tools/javac/annotations/8218152/MalformedAnnotationProcessorTests.java +++ b/test/langtools/tools/javac/annotations/8218152/MalformedAnnotationProcessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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 @@ -91,8 +91,8 @@ public class MalformedAnnotationProcessorTests extends TestRunner{ .getOutputLines(Task.OutputKind.DIRECT); System.out.println(actualErrors.get(0)); - if (!actualErrors.get(0).contains("- compiler.err.proc.cant.load.class: " + - "Incompatible magic value")) { + if (!actualErrors.get(0).contains("- compiler.err.proc.bad.config.file: " + + "javax.annotation.processing.Processor: Provider BadAnnoProcessor not found")) { throw new AssertionError("Unexpected errors reported: " + actualErrors); } } @@ -162,8 +162,8 @@ public class MalformedAnnotationProcessorTests extends TestRunner{ .writeAll() .getOutputLines(Task.OutputKind.DIRECT); - if (!actualErrors.get(0).contains("- compiler.err.proc.cant.load.class: " + - "WrongClassFileVersion has been compiled by a more recent version")) { + if (!actualErrors.get(0).contains("- compiler.err.proc.bad.config.file: " + + "javax.annotation.processing.Processor: Provider WrongClassFileVersion not found")) { throw new AssertionError("Unexpected errors reported: " + actualErrors); } } diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/TypeAnnotationsOnTypes.java b/test/langtools/tools/javac/annotations/typeAnnotations/TypeAnnotationsOnTypes.java new file mode 100644 index 00000000000..5ae6b17a24c --- /dev/null +++ b/test/langtools/tools/javac/annotations/typeAnnotations/TypeAnnotationsOnTypes.java @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2026, 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. + */ + +/* + * @test + * @bug 8374020 + * @summary Verify types are set back to the AST. + * @library /tools/lib + * @modules + * jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * @build toolbox.ToolBox toolbox.JavacTask + * @compile TypeAnnotationsOnTypes.java + * @run main TypeAnnotationsOnTypes + */ + +import com.sun.source.tree.VariableTree; +import com.sun.source.util.TaskEvent; +import com.sun.source.util.TaskListener; +import com.sun.source.util.TreePath; +import com.sun.source.util.TreePathScanner; +import com.sun.source.util.Trees; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.type.UnionType; +import toolbox.JavacTask; +import toolbox.ToolBox; + +public class TypeAnnotationsOnTypes { + + public static void main(String... args) throws Exception { + new TypeAnnotationsOnTypes().run(); + } + + ToolBox tb = new ToolBox(); + + void run() throws Exception { + typeAnnotationInConstantExpressionFieldInit(Paths.get(".")); + } + + void typeAnnotationInConstantExpressionFieldInit(Path base) throws Exception { + Path src = base.resolve("src"); + Path classes = base.resolve("classes"); + tb.writeJavaFiles(src, + """ + import java.lang.annotation.ElementType; + import java.util.List; + import java.lang.annotation.Target; + + class Test { + + void f() { + @TA List l1; + @TA String[] l2; + @TA TypeVar l3; + try { + } catch (@TA IllegalStateException | NullPointerException | @TA IllegalArgumentException ex) {} + } + + @Target(ElementType.TYPE_USE) + @interface TA {} + } + """); + Files.createDirectories(classes); + List actual = new ArrayList<>(); + new JavacTask(tb) + .options("-d", classes.toString()) + .files(tb.findJavaFiles(src)) + .callback(task -> { + task.addTaskListener(new TaskListener() { + @Override + public void finished(TaskEvent e) { + if (e.getKind() != TaskEvent.Kind.ANALYZE) { + return ; + } + Trees trees = Trees.instance(task); + new TreePathScanner() { + @Override + public Void visitVariable(VariableTree node, Void p) { + TreePath typePath = + new TreePath(getCurrentPath(), node.getType()); + actual.add(node.getName() + + ": type on variable: " + + typeToString(trees.getTypeMirror(getCurrentPath())) + + ": type on type: " + + typeToString(trees.getTypeMirror(typePath))); + return super.visitVariable(node, p); + } + }.scan(e.getCompilationUnit(), null); + } + }); + }) + .run() + .writeAll(); + + List expected = List.of( + "l1: type on variable: java.util.@Test.TA List: type on type: java.util.@Test.TA List", + "l2: type on variable: java.lang.@Test.TA String[]: type on type: java.lang.@Test.TA String[]", + "l3: type on variable: @Test.TA TypeVar: type on type: @Test.TA TypeVar", + "ex: type on variable: java.lang.@Test.TA IllegalStateException | java.lang.NullPointerException | java.lang.@Test.TA IllegalArgumentException: " + + "type on type: java.lang.@Test.TA IllegalStateException | java.lang.NullPointerException | java.lang.@Test.TA IllegalArgumentException" + ); + + actual.forEach(System.out::println); + if (!expected.equals(actual)) { + throw new AssertionError("Expected: " + expected + ", but got: " + actual); + } + } + + static String typeToString(TypeMirror type) { + if (type != null && type.getKind() == TypeKind.UNION) { + return ((UnionType) type).getAlternatives().stream().map(t -> typeToString(t)).collect(Collectors.joining(" | ")); + } else { + return String.valueOf(type); + } + } +} diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/TypeAnnotationsOnVariables.java b/test/langtools/tools/javac/annotations/typeAnnotations/TypeAnnotationsOnVariables.java index 2e68e18f8f7..e5a1e5650d3 100644 --- a/test/langtools/tools/javac/annotations/typeAnnotations/TypeAnnotationsOnVariables.java +++ b/test/langtools/tools/javac/annotations/typeAnnotations/TypeAnnotationsOnVariables.java @@ -23,15 +23,16 @@ /* * @test - * @bug 8371155 + * @bug 8371155 8379550 * @summary Verify type annotations on local-like variables are propagated to * their types at an appropriate time. * @library /tools/lib * @modules * jdk.compiler/com.sun.tools.javac.api * jdk.compiler/com.sun.tools.javac.main + * jdk.jdeps/com.sun.tools.javap * @build toolbox.ToolBox toolbox.JavacTask - * @run main TypeAnnotationsOnVariables + * @run junit TypeAnnotationsOnVariables */ import com.sun.source.tree.LambdaExpressionTree; @@ -41,31 +42,44 @@ import com.sun.source.util.TaskEvent; import com.sun.source.util.TaskListener; import com.sun.source.util.TreePathScanner; import com.sun.source.util.Trees; +import java.io.IOException; +import java.lang.classfile.Attributes; +import java.lang.classfile.ClassFile; +import java.lang.classfile.ClassModel; +import java.lang.classfile.MethodModel; +import java.lang.classfile.attribute.RuntimeInvisibleTypeAnnotationsAttribute; +import java.lang.classfile.constantpool.ConstantPool; +import java.lang.classfile.constantpool.Utf8Entry; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.stream.Collectors; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.type.UnionType; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; import toolbox.JavacTask; +import toolbox.JavapTask; +import toolbox.Task; import toolbox.ToolBox; public class TypeAnnotationsOnVariables { - public static void main(String... args) throws Exception { - new TypeAnnotationsOnVariables().run(); - } + private static final Pattern CP_REFERENCE = Pattern.compile("#([1-9][0-9]*)"); + final ToolBox tb = new ToolBox(); + Path base; - ToolBox tb = new ToolBox(); - - void run() throws Exception { - typeAnnotationInConstantExpressionFieldInit(Paths.get(".")); - } - - void typeAnnotationInConstantExpressionFieldInit(Path base) throws Exception { + @Test + void typeAnnotationInConstantExpressionFieldInit() throws Exception { Path src = base.resolve("src"); Path classes = base.resolve("classes"); tb.writeJavaFiles(src, @@ -203,9 +217,208 @@ public class TypeAnnotationsOnVariables { } static String treeToString(Tree tree) { - if (tree.toString().contains("\n")) { - System.err.println("!!!"); - } return String.valueOf(tree).replaceAll("\\R", " "); } + + @Test + void properPathForLocalVarsInLambdas() throws Exception { + Path src = base.resolve("src"); + Path classes = base.resolve("classes"); + tb.writeJavaFiles(src, + """ + import java.lang.annotation.ElementType; + import java.lang.annotation.Target; + import java.util.function.Supplier; + + class Test { + @Target(ElementType.TYPE_USE) + @interface TypeAnno { } + + void o() { + Runnable r = () -> { + @TypeAnno long test1 = 0; + while (true) { + @TypeAnno long test2 = 0; + System.err.println(test2); + try (@TypeAnno AutoCloseable ac = null) { + System.err.println(ac); + } catch (@TypeAnno Exception e1) { + System.err.println(e1); + } + try { + "".length(); + } catch (@TypeAnno final Exception e2) { + System.err.println(e2); + } + try { + "".length(); + } catch (@TypeAnno IllegalStateException | @TypeAnno NullPointerException | IllegalArgumentException e3) { + System.err.println(e3); + } + Runnable r2 = () -> { + @TypeAnno long test3 = 0; + while (true) { + @TypeAnno long test4 = 0; + System.err.println(test4); + } + }; + Object o = null; + if (o instanceof @TypeAnno String s) { + System.err.println(s); + } + } + }; + } + void lambdaInClass() { + class C { + Runnable r = () -> { + @TypeAnno long test1 = 0; + System.err.println(test1); + }; + } + } + void classInLambda() { + Runnable r = () -> { + class C { + void method() { + @TypeAnno long test1 = 0; + System.err.println(test1); + } + } + }; + } + } + """); + Files.createDirectories(classes); + new JavacTask(tb) + .options("-d", classes.toString()) + .files(tb.findJavaFiles(src)) + .run() + .writeAll(); + + Path testClass = classes.resolve("Test.class"); + TestClassDesc testClassDesc = TestClassDesc.create(testClass); + MethodModel oMethod = singletonValue(testClassDesc.name2Method().get("o")); + var oTypeAnnos = getAnnotations(oMethod); + assertFalse(oTypeAnnos.isPresent(), () -> oTypeAnnos.toString()); + + checkTypeAnnotations(testClassDesc, + "lambda$o$0", + " 0: LTest$TypeAnno;(): LOCAL_VARIABLE, {start_pc=2, length=151, index=0}", + " Test$TypeAnno", + " 1: LTest$TypeAnno;(): LOCAL_VARIABLE, {start_pc=4, length=146, index=2}", + " Test$TypeAnno", + " 2: LTest$TypeAnno;(): RESOURCE_VARIABLE, {start_pc=14, length=52, index=4}", + " Test$TypeAnno", + " 3: LTest$TypeAnno;(): EXCEPTION_PARAMETER, exception_index=2", + " Test$TypeAnno", + " 4: LTest$TypeAnno;(): EXCEPTION_PARAMETER, exception_index=3", + " Test$TypeAnno", + " 5: LTest$TypeAnno;(): EXCEPTION_PARAMETER, exception_index=4", + " Test$TypeAnno", + " 6: LTest$TypeAnno;(): EXCEPTION_PARAMETER, exception_index=5", + " Test$TypeAnno", + " 7: LTest$TypeAnno;(): LOCAL_VARIABLE, {start_pc=142, length=8, index=6}", + " Test$TypeAnno"); + + checkTypeAnnotations(testClassDesc, + "lambda$o$1", + " 0: LTest$TypeAnno;(): LOCAL_VARIABLE, {start_pc=2, length=12, index=0}", + " Test$TypeAnno", + " 1: LTest$TypeAnno;(): LOCAL_VARIABLE, {start_pc=4, length=7, index=2}", + " Test$TypeAnno"); + + checkTypeAnnotations(testClassDesc, + "lambda$classInLambda$0"); + + checkTypeAnnotations(TestClassDesc.create(classes.resolve("Test$1C.class")), + "lambda$new$0", + " 0: LTest$TypeAnno;(): LOCAL_VARIABLE, {start_pc=2, length=8, index=0}", + " Test$TypeAnno"); + } + + private void checkTypeAnnotations(TestClassDesc testClassDesc, + String lambdaMethodName, + String... expectedEntries) throws IOException { + MethodModel lambdaMethod = singletonValue(testClassDesc.name2Method().get(lambdaMethodName)); + var lambdaTypeAnnos = getAnnotations(lambdaMethod); + if (expectedEntries.length == 0) { + assertFalse(lambdaTypeAnnos.isPresent(), () -> lambdaTypeAnnos.toString()); + } else { + assertTrue(lambdaTypeAnnos.isPresent(), () -> lambdaTypeAnnos.toString()); + assertEquals(expectedEntries.length / 2, + lambdaTypeAnnos.orElseThrow().annotations().size(), + () -> lambdaTypeAnnos.orElseThrow().annotations().toString()); + + checkJavapOutput(testClassDesc, + List.of(expectedEntries)); + } + } + + private T singletonValue(List values) { + assertEquals(1, values.size()); + return values.get(0); + } + + private Optional getAnnotations(MethodModel m) { + return m.findAttribute(Attributes.code()) + .orElseThrow() + .findAttribute(Attributes.runtimeInvisibleTypeAnnotations()); + } + + void checkJavapOutput(TestClassDesc testClassDesc, List expectedOutput) throws IOException { + String javapOut = new JavapTask(tb) + .options("-v", "-p") + .classes(testClassDesc.pathToClass().toString()) + .run() + .getOutput(Task.OutputKind.DIRECT); + + StringBuilder expandedJavapOutBuilder = new StringBuilder(); + Matcher m = CP_REFERENCE.matcher(javapOut); + + while (m.find()) { + String cpIndexText = m.group(1); + int cpIndex = Integer.parseInt(cpIndexText); + m.appendReplacement(expandedJavapOutBuilder, Matcher.quoteReplacement(testClassDesc.cpIndex2Name().getOrDefault(cpIndex, cpIndexText))); + } + + m.appendTail(expandedJavapOutBuilder); + + String expandedJavapOut = expandedJavapOutBuilder.toString(); + + for (String expected : expectedOutput) { + if (!expandedJavapOut.contains(expected)) { + System.err.println(expandedJavapOut); + throw new AssertionError("unexpected output"); + } + } + } + + record TestClassDesc(Path pathToClass, + Map> name2Method, + Map cpIndex2Name) { + public static TestClassDesc create(Path pathToClass) throws IOException{ + ClassModel model = ClassFile.of().parse(pathToClass); + Map> name2Method = + model.methods() + .stream() + .collect(Collectors.groupingBy(m -> m.methodName().stringValue())); + ConstantPool cp = model.constantPool(); + int cpSize = cp.size(); + Map cpIndex2Name = new HashMap<>(); + + for (int i = 1; i < cpSize; i++) { + if (cp.entryByIndex(i) instanceof Utf8Entry string) { + cpIndex2Name.put(i, string.stringValue()); + } + } + + return new TestClassDesc(pathToClass, name2Method, cpIndex2Name); + } + } + + @BeforeEach + void setUp(TestInfo thisTest) { + base = Path.of(thisTest.getTestMethod().orElseThrow().getName()); + } } diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/classfile/LocalClassesTest.java b/test/langtools/tools/javac/annotations/typeAnnotations/classfile/LocalClassesTest.java new file mode 100644 index 00000000000..66b55c8d209 --- /dev/null +++ b/test/langtools/tools/javac/annotations/typeAnnotations/classfile/LocalClassesTest.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2026, 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. + */ + +/* + * @test + * @bug 8371817 + * @summary Check for type annotating types that refer to local classes read + * from classfiles + * @library /tools/lib + * @modules + * jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * @build toolbox.ToolBox toolbox.JavacTask + * @run junit LocalClassesTest + */ + +import com.sun.source.tree.ClassTree; +import com.sun.source.util.TaskEvent; +import com.sun.source.util.TaskListener; +import com.sun.source.util.TreePathScanner; +import com.sun.source.util.Trees; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.ElementFilter; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; +import toolbox.JavacTask; +import toolbox.ToolBox; + +public class LocalClassesTest { + + ToolBox tb = new ToolBox(); + Path base; + + @Test + void test() throws Exception { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + + Map local2enclosing = new HashMap<>(); + new JavacTask(tb) + .options("-d", classes.toString()) + .sources(""" + import java.lang.annotation.ElementType; + import java.lang.annotation.Target; + + public class Test { + public static void m1() { + class Local1 { + @Nullable Local1 l; + } + } + public void m2() { + class Local2 { + @Nullable Local2 l; + } + } + } + + @Target({ElementType.TYPE_USE}) + @interface Nullable {} + """) + .callback(task -> { + task.addTaskListener(new TaskListener() { + @Override + public void finished(TaskEvent e) { + if (e.getKind() == TaskEvent.Kind.ANALYZE) { + Trees trees = Trees.instance(task); + new TreePathScanner<>() { + @Override + public Object visitClass(ClassTree node, Object p) { + if (node.getSimpleName().toString().startsWith("Local")) { + Element el = trees.getElement(getCurrentPath()); + TypeMirror type = trees.getTypeMirror(getCurrentPath()); + local2enclosing.put(el.getSimpleName().toString(), ((DeclaredType) type).getEnclosingType().toString()); + } + return super.visitClass(node, p); + } + }.scan(e.getCompilationUnit(), null); + } + } + }); + }) + .run() + .writeAll(); + + Path classes2 = base.resolve("classes2"); + Files.createDirectories(classes2); + + ProcessorImpl p = new ProcessorImpl(); + new JavacTask(tb) + .options("-cp", classes.toString(), "-d", classes2.toString()) + .processors(p) + .classes("Test$1Local1", "Test$1Local2") + .run() + .writeAll(); + + Assertions.assertEquals(local2enclosing.get("Local1"), p.local2enclosing.get("Local1")); + Assertions.assertEquals(local2enclosing.get("Local2"), p.local2enclosing.get("Local2")); + } + + @SupportedAnnotationTypes("*") + private static class ProcessorImpl extends AbstractProcessor { + private Map local2enclosing = new HashMap<>(); + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + for (TypeElement te : ElementFilter.typesIn(roundEnv.getRootElements())) { + if (te.getSimpleName().toString().startsWith("Local")) { + local2enclosing.put(te.getSimpleName().toString(), ((DeclaredType) te.asType()).getEnclosingType().toString()); + } + } + return false; + } + + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latestSupported(); + } + } + + @BeforeEach + public void setup(TestInfo info) { + base = Paths.get(".") + .resolve(info.getTestMethod() + .orElseThrow() + .getName()); + } +} diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/failures/CantAnnotatePackages.java b/test/langtools/tools/javac/annotations/typeAnnotations/failures/CantAnnotatePackages.java index d35351b6f40..5031a74bc5a 100644 --- a/test/langtools/tools/javac/annotations/typeAnnotations/failures/CantAnnotatePackages.java +++ b/test/langtools/tools/javac/annotations/typeAnnotations/failures/CantAnnotatePackages.java @@ -1,6 +1,6 @@ /* * @test /nodynamiccopyright/ - * @bug 8026564 8043226 8334055 + * @bug 8026564 8043226 8334055 8179187 * @summary The parts of a fully-qualified type can't be annotated. * @author Werner Dietl * @compile/fail/ref=CantAnnotatePackages.out -XDrawDiagnostics CantAnnotatePackages.java diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/failures/CantAnnotatePackages.out b/test/langtools/tools/javac/annotations/typeAnnotations/failures/CantAnnotatePackages.out index b91d65828b9..6e2b9cb93b0 100644 --- a/test/langtools/tools/javac/annotations/typeAnnotations/failures/CantAnnotatePackages.out +++ b/test/langtools/tools/javac/annotations/typeAnnotations/failures/CantAnnotatePackages.out @@ -1,5 +1,5 @@ -CantAnnotatePackages.java:16:14: compiler.err.cant.resolve.location: kindname.class, java, , , (compiler.misc.location: kindname.class, CantAnnotatePackages, null) -CantAnnotatePackages.java:17:9: compiler.err.cant.resolve.location: kindname.class, lang, , , (compiler.misc.location: kindname.package, java, null) -CantAnnotatePackages.java:18:14: compiler.err.cant.resolve.location: kindname.class, lang, , , (compiler.misc.location: kindname.package, java, null) CantAnnotatePackages.java:14:18: compiler.err.type.annotation.inadmissible: (compiler.misc.type.annotation.1: @TA), java.lang, @TA java.lang.Object +CantAnnotatePackages.java:16:14: compiler.err.type.annotation.inadmissible: (compiler.misc.type.annotation.1: @TA), java.lang, @TA java.lang.Object +CantAnnotatePackages.java:17:9: compiler.err.type.annotation.inadmissible: (compiler.misc.type.annotation.1: @TA), java.lang, @TA java.lang.Object +CantAnnotatePackages.java:18:14: compiler.err.type.annotation.inadmissible: (compiler.misc.type.annotation.1: @TA), java.lang, @TA java.lang.Object 4 errors diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/failures/CantAnnotateScoping.java b/test/langtools/tools/javac/annotations/typeAnnotations/failures/CantAnnotateScoping.java index 4bdd791909c..427c1fef3a8 100644 --- a/test/langtools/tools/javac/annotations/typeAnnotations/failures/CantAnnotateScoping.java +++ b/test/langtools/tools/javac/annotations/typeAnnotations/failures/CantAnnotateScoping.java @@ -1,6 +1,6 @@ /* * @test /nodynamiccopyright/ - * @bug 8006733 8006775 8043226 8334055 + * @bug 8006733 8006775 8043226 8334055 8179187 * @summary Ensure behavior for nested types is correct. * @author Werner Dietl * @compile/fail/ref=CantAnnotateScoping.out -XDrawDiagnostics CantAnnotateScoping.java diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/failures/CantAnnotateScoping.out b/test/langtools/tools/javac/annotations/typeAnnotations/failures/CantAnnotateScoping.out index ade5333a446..2ae736ad315 100644 --- a/test/langtools/tools/javac/annotations/typeAnnotations/failures/CantAnnotateScoping.out +++ b/test/langtools/tools/javac/annotations/typeAnnotations/failures/CantAnnotateScoping.out @@ -1,12 +1,13 @@ -CantAnnotateScoping.java:63:9: compiler.err.cant.resolve.location: kindname.class, lang, , , (compiler.misc.location: kindname.package, java, null) -CantAnnotateScoping.java:68:9: compiler.err.cant.resolve.location: kindname.class, XXX, , , (compiler.misc.location: kindname.package, java, null) -CantAnnotateScoping.java:71:9: compiler.err.cant.resolve.location: kindname.class, lang, , , (compiler.misc.location: kindname.package, java, null) +CantAnnotateScoping.java:68:18: compiler.err.doesnt.exist: java.XXX CantAnnotateScoping.java:38:14: compiler.err.type.annotation.inadmissible: (compiler.misc.type.annotation.1: @TA), Test.Outer, @TA Test.Outer.SInner CantAnnotateScoping.java:51:18: compiler.err.type.annotation.inadmissible: (compiler.misc.type.annotation.1: @TA), java.lang, @TA java.lang.Object CantAnnotateScoping.java:60:37: compiler.err.type.annotation.inadmissible: (compiler.misc.type.annotation: @TA,@TA2), java.lang, @DTA @TA @TA2 java.lang.Object CantAnnotateScoping.java:40:14: compiler.err.type.annotation.inadmissible: (compiler.misc.type.annotation.1: @TA), Test.Outer, @TA Test.Outer.SInner +CantAnnotateScoping.java:63:11: compiler.err.annotation.type.not.applicable.to.type: DA +CantAnnotateScoping.java:68:11: compiler.err.annotation.type.not.applicable.to.type: DA +CantAnnotateScoping.java:71:9: compiler.err.type.annotation.inadmissible: (compiler.misc.type.annotation.1: @TA), java.lang, @TA java.lang.Object CantAnnotateScoping.java:44:34: compiler.err.type.annotation.inadmissible: (compiler.misc.type.annotation: @TA,@TA2), Test.Outer, @TA @TA2 Test.Outer.SInner CantAnnotateScoping.java:44:25: compiler.err.annotation.type.not.applicable.to.type: DA CantAnnotateScoping.java:48:38: compiler.err.type.annotation.inadmissible: (compiler.misc.type.annotation.1: @TA), Test.Outer, @TA Test.Outer.SInner CantAnnotateScoping.java:48:34: compiler.err.annotation.type.not.applicable.to.type: DA -11 errors +12 errors diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/failures/MethodArguments.java b/test/langtools/tools/javac/annotations/typeAnnotations/failures/MethodArguments.java new file mode 100644 index 00000000000..e539721855b --- /dev/null +++ b/test/langtools/tools/javac/annotations/typeAnnotations/failures/MethodArguments.java @@ -0,0 +1,47 @@ +/* + * @test /nodynamiccopyright/ + * @summary Omit type-use annotations from diagnostics + * @compile/fail/ref=MethodArguments.out -XDrawDiagnostics MethodArguments.java p/A.java p/B.java + */ + +import java.util.List; +import p.A; +import p.B; + +public final class MethodArguments { + public static void main(String[] args) { + // error non-static.cant.be.ref: + // non-static ... cannot be referenced from a static context + B.one("bar"); + + B b = new B(); + + // error ref.ambiguous: + // reference to ... is ambiguous + // ... + // both ... and ... match + b.one(null); + + // error report.access: + // ... has private access in ... + b.two("foo"); + // ... has protected access in ... + b.three("foo"); + + // error not.def.public.cant.access: + // ... is not public in ... cannot be accessed from outside package + b.four("foo"); + } + + void five(@A String s) { + } + + void five(@A String s) { + } + + void six(List<@A String> s) { + } + + void six(List<@A String> s) { + } +} diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/failures/MethodArguments.out b/test/langtools/tools/javac/annotations/typeAnnotations/failures/MethodArguments.out new file mode 100644 index 00000000000..b4ecee21403 --- /dev/null +++ b/test/langtools/tools/javac/annotations/typeAnnotations/failures/MethodArguments.out @@ -0,0 +1,8 @@ +MethodArguments.java:39:8: compiler.err.already.defined: kindname.method, five(java.lang.String), kindname.class, MethodArguments +MethodArguments.java:45:8: compiler.err.already.defined: kindname.method, six(java.util.List), kindname.class, MethodArguments +MethodArguments.java:15:6: compiler.err.non-static.cant.be.ref: kindname.method, one(java.lang.String) +MethodArguments.java:23:6: compiler.err.ref.ambiguous: one, kindname.method, one(java.lang.String), p.B, kindname.method, one(java.lang.Integer), p.B +MethodArguments.java:27:6: compiler.err.report.access: two(java.lang.String), private, p.B +MethodArguments.java:29:6: compiler.err.report.access: three(java.lang.String), protected, p.B +MethodArguments.java:33:6: compiler.err.not.def.public.cant.access: four(java.lang.String), p.B +7 errors diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/failures/p/A.java b/test/langtools/tools/javac/annotations/typeAnnotations/failures/p/A.java new file mode 100644 index 00000000000..9e3bb15dab5 --- /dev/null +++ b/test/langtools/tools/javac/annotations/typeAnnotations/failures/p/A.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2026, Google LLC. 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. + */ + +package p; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE_USE) +public @interface A {} diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/failures/p/B.java b/test/langtools/tools/javac/annotations/typeAnnotations/failures/p/B.java new file mode 100644 index 00000000000..4dfe16b312d --- /dev/null +++ b/test/langtools/tools/javac/annotations/typeAnnotations/failures/p/B.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2026, Google LLC. 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. + */ + +package p; + +public class B { + public void one(@A String s) { + } + + public void one(@A Integer i) { + } + + private void two(@A String s) { + } + + protected void three(@A String s) { + } + + void four(@A String s) { + } +} diff --git a/test/langtools/tools/javac/implicitCompile/APImplicitClassesWarnings.java b/test/langtools/tools/javac/implicitCompile/APImplicitClassesWarnings.java index dc60c6cc78a..8c1e356217c 100644 --- a/test/langtools/tools/javac/implicitCompile/APImplicitClassesWarnings.java +++ b/test/langtools/tools/javac/implicitCompile/APImplicitClassesWarnings.java @@ -23,7 +23,7 @@ /* * @test - * @bug 8378740 + * @bug 8378740 8378950 * @summary Verify warnings are properly suppressed for the combination of * annotation processing and implicit compilation * @library /tools/lib @@ -96,9 +96,7 @@ public class APImplicitClassesWarnings { List expected = List.of( "Use.java:4:5: compiler.warn.has.been.deprecated.for.removal: test.Depr, test", - "Use.java:4:5: compiler.warn.has.been.deprecated.for.removal: test.Depr, test", - "Use.java:4:5: compiler.warn.has.been.deprecated.for.removal: test.Depr, test", - "3 warnings" + "1 warning" ); tb.checkEqual(expected, log); @@ -143,6 +141,53 @@ public class APImplicitClassesWarnings { .writeAll(); } + @Test + public void testCorrectImport() throws Exception { + Path src = base.resolve("src"); + Path classes = base.resolve("classes"); + tb.writeJavaFiles(src, + """ + package test; + + @Deprecated(forRemoval=true) + public class Depr { + public static class Nested {} + } + """, + """ + package test; + import test.Depr.Nested; + public class Use { + Implicit implicit; + Nested nest; + } + """, + """ + package test; + public interface Implicit {} + """); + Files.createDirectories(classes); + + List log = new JavacTask(tb) + .options("-d", classes.toString(), + "-XDrawDiagnostics", + "-implicit:class", + "-sourcepath", src.toString()) + .files(src.resolve("test").resolve("Depr.java"), + src.resolve("test").resolve("Use.java")) + .processors(new ProcessorImpl()) + .run() + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + List expected = List.of( + "Use.java:2:12: compiler.warn.has.been.deprecated.for.removal: test.Depr, test", + "1 warning" + ); + + tb.checkEqual(expected, log); + } + @SupportedAnnotationTypes("*") private static class ProcessorImpl extends AbstractProcessor { @Override diff --git a/test/langtools/tools/javac/lexer/AsciiSubCharTest.java b/test/langtools/tools/javac/lexer/AsciiSubCharTest.java new file mode 100644 index 00000000000..0c96b744d72 --- /dev/null +++ b/test/langtools/tools/javac/lexer/AsciiSubCharTest.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2026, 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. + */ + +/** + * @test + * @bug 8371873 + * @summary Check for proper handling of trailing ASCII SUB character + * @library /tools/lib + * @modules + * jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * @build toolbox.ToolBox toolbox.JavacTask + * @run junit AsciiSubCharTest + */ + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; + +import toolbox.JavacTask; +import toolbox.Task; +import toolbox.ToolBox; + +public class AsciiSubCharTest { + + ToolBox tb = new ToolBox(); + Path base; + + @Test + public void testTrailingAsciiSubIsIgnored() throws Exception { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + new JavacTask(tb) + .options("-d", classes.toString()) + .sources(""" + public class Test { + void main(String... args) { IO.println("\u001A"); } + } + \u001A""") + .run() + .writeAll(); + } + + @Test + public void testMultipleTrailingAsciiSubAreReported() throws Exception { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + List out = new JavacTask(tb) + .options("-d", classes.toString(), "-XDrawDiagnostics", "-nowarn") + .sources(""" + public class Test { + void main(String... args) { IO.println("\u001A"); } + } + \u001A\u001A""") + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + tb.checkEqual(out, List.of( + "Test.java:4:1: compiler.err.illegal.char: \\u001a", + "Test.java:4:2: compiler.err.premature.eof", + "2 errors")); + } + + @Test + public void test8371873() throws Exception { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + List out = new JavacTask(tb) + .options("-d", classes.toString(), "-XDrawDiagnostics", "-nowarn") + .sources(""" + public class Test { + void main(String... args) { IO.println("\u001A"); } + } + \u001A\u0001""") + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + tb.checkEqual(out, List.of( + "Test.java:4:1: compiler.err.illegal.char: \\u001a", + "Test.java:4:2: compiler.err.illegal.char: \\u0001", + "Test.java:4:3: compiler.err.premature.eof", + "3 errors")); + } + + @BeforeEach + public void setUp(TestInfo info) { + base = Paths.get(".") + .resolve(info.getTestMethod() + .orElseThrow() + .getName()); + } +} diff --git a/test/langtools/tools/javac/modules/IncubatingTest.java b/test/langtools/tools/javac/modules/IncubatingTest.java index 68f615abc04..6ff1a0a29c8 100644 --- a/test/langtools/tools/javac/modules/IncubatingTest.java +++ b/test/langtools/tools/javac/modules/IncubatingTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -23,7 +23,7 @@ /* * @test - * @bug 8171177 8187591 + * @bug 8171177 8187591 8378950 * @summary Verify that ModuleResolution attribute flags are honored. * @library /tools/lib * @modules jdk.compiler/com.sun.tools.javac.api @@ -46,10 +46,15 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; - +import java.util.Set; import java.lang.classfile.*; import java.lang.classfile.attribute.ModuleResolutionAttribute; import java.lang.classfile.constantpool.*; +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.TypeElement; import toolbox.JavacTask; import toolbox.Task; import toolbox.Task.Expect; @@ -242,6 +247,29 @@ public class IncubatingTest extends ModuleTestBase { .outdir(testModuleClasses) .files(findJavaFiles(testModuleSrc)) .run(Expect.SUCCESS); + + //test with annotation processing + log = new JavacTask(tb) + .options("--module-path", classes.toString(), + "-XDrawDiagnostics", + "-Werror") + .outdir(testModuleClasses) + .files(findJavaFiles(testModuleSrc)) + .processors(new ProcessorImpl()) + .run(Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + expected = Arrays.asList( + "- compiler.warn.incubating.modules: jdk.i", + "- compiler.err.warnings.and.werror", + "1 error", + "1 warning" + ); + + if (!expected.equals(log)) { + throw new AssertionError("Unexpected output: " + log); + } } private void copyJavaBase(Path targetDir) throws IOException { @@ -270,4 +298,16 @@ public class IncubatingTest extends ModuleTestBase { out.write(newBytes); } } + + @SupportedAnnotationTypes("*") + private static class ProcessorImpl extends AbstractProcessor { + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + return false; + } + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latest(); + } + } } diff --git a/test/langtools/tools/javac/processing/rounds/OverwriteBetweenCompilations_2.out b/test/langtools/tools/javac/processing/rounds/OverwriteBetweenCompilations_2.out index 826e2b4bcb0..431fd3d9079 100644 --- a/test/langtools/tools/javac/processing/rounds/OverwriteBetweenCompilations_2.out +++ b/test/langtools/tools/javac/processing/rounds/OverwriteBetweenCompilations_2.out @@ -53,5 +53,3 @@ public abstract class GeneratedClass extends java.ut public void test(long a); } -- compiler.note.deprecated.filename: OverwriteBetweenCompilationsSource.java -- compiler.note.deprecated.recompile diff --git a/test/langtools/tools/javac/processing/warnings/TestParserWarnings.java b/test/langtools/tools/javac/processing/warnings/TestParserWarnings.java new file mode 100644 index 00000000000..b611e7a8655 --- /dev/null +++ b/test/langtools/tools/javac/processing/warnings/TestParserWarnings.java @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2026, 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. + */ + +/* + * @test + * @bug 8381654 + * @summary AP interference with tokenizer warnings + * @library /tools/lib + * @modules + * jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * @build toolbox.ToolBox toolbox.JavacTask + * @run junit ${test.main.class} + */ + +import java.io.IOException; +import java.io.Writer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Set; + +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.Filer; +import javax.annotation.processing.Messager; +import javax.annotation.processing.ProcessingEnvironment; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.TypeElement; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import toolbox.JavacTask; +import toolbox.Task; +import toolbox.ToolBox; + +public class TestParserWarnings { + + static boolean[] apOptions() { + return new boolean[] {false, true}; + } + + final ToolBox tb = new ToolBox(); + Path base, src, classes; + + @ParameterizedTest @MethodSource("apOptions") + public void testPreviewWarning(boolean useProcessor) throws Exception { + tb.writeJavaFiles(src, """ + public record MyRec() {} + """); + + JavacTask task = new JavacTask(tb) + .options("--enable-preview", + "-source", Integer.toString(Runtime.version().feature()), + "-XDforcePreview", + "-XDrawDiagnostics") + .files(tb.findJavaFiles(src)) + .outdir(classes); + if (useProcessor) { + task.processors(new ProcessorImpl()); + } + List log = task + .run() + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + List expected = List.of( + "- compiler.note.preview.filename: MyRec.java, DEFAULT", + "- compiler.note.preview.recompile" + ); + + tb.checkEqual(expected, log); + } + + @ParameterizedTest @MethodSource("apOptions") + public void testTextBlockWarning(boolean useProcessor) throws Exception { + tb.writeJavaFiles(src, """ + class TextBlockWhitespace { + String m() { + return ""\" + \\u0009\\u0009\\u0009\\u0009tab indentation + \\u0020\\u0020\\u0020\\u0020space indentation and trailing space\\u0020 + \\u0020\\u0020\\u0020\\u0020""\"; + } + } + """); + + JavacTask task = new JavacTask(tb) + .options("-Xlint:text-blocks", + "-XDrawDiagnostics") + .files(tb.findJavaFiles(src)) + .outdir(classes); + if (useProcessor) { + task.processors(new ProcessorImpl()); + } + List log = task + .run() + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + List expected = List.of( + "TextBlockWhitespace.java:3:16: compiler.warn.inconsistent.white.space.indentation", + "TextBlockWhitespace.java:3:16: compiler.warn.trailing.white.space.will.be.removed", + "2 warnings" + ); + + tb.checkEqual(expected, log); + } + + @Test + public void testAPGeneratedSource() throws Exception { + tb.writeJavaFiles(src, """ + import java.lang.annotation.ElementType; + import java.lang.annotation.Target; + + @A + class Test {} + + @Target(ElementType.TYPE) + @interface A {} + """); + + List log = new JavacTask(tb) + .options("-Xlint:text-blocks", + "-XDrawDiagnostics") + .files(tb.findJavaFiles(src)) + .outdir(classes) + .processors(new ProcessorImpl()) + .run() + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + List expected = List.of( + "Generated.java:3:16: compiler.warn.inconsistent.white.space.indentation", + "Generated.java:3:16: compiler.warn.trailing.white.space.will.be.removed", + "2 warnings" + ); + + tb.checkEqual(expected, log); + } + + @SupportedAnnotationTypes("*") + private static class ProcessorImpl extends AbstractProcessor { + private boolean done = false; + private Filer filer; + private Messager msgr; + + @Override + public void init(ProcessingEnvironment env) { + filer = env.getFiler(); + msgr = env.getMessager(); + } + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + if (!done && !annotations.isEmpty()) { + try (Writer pw = filer.createSourceFile("Generated").openWriter()) { + pw.write(""" + public class Generated { + String m() { + return ""\" + \\u0009\\u0009\\u0009\\u0009tab indentation + \\u0020\\u0020\\u0020\\u0020space indentation and trailing space\\u0020 + \\u0020\\u0020\\u0020\\u0020""\"; + } + } + """); + pw.flush(); + pw.close(); + done = true; + } catch (IOException ioe) { + msgr.printError(ioe.getMessage()); + return false; + } + return true; + } + return false; + } + + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latest(); + } + } + + @BeforeEach + public void setUp(TestInfo info) throws Exception { + base = Path.of(".").resolve(info.getTestMethod().get().getName()); + if (Files.exists(base)) { + tb.cleanDirectory(base); + } + src = base.resolve("src"); + classes = base.resolve("classes"); + Files.createDirectories(classes); + } +} diff --git a/test/langtools/tools/javac/warnings/DeprecatedNoEffect.java b/test/langtools/tools/javac/warnings/DeprecatedNoEffect.java new file mode 100644 index 00000000000..b2c8a10c0ac --- /dev/null +++ b/test/langtools/tools/javac/warnings/DeprecatedNoEffect.java @@ -0,0 +1,14 @@ +/* + * @test /nodynamiccopyright/ + * @bug 8382368 + * @compile/ref=DeprecatedNoEffect.out -Xlint:deprecation -XDrawDiagnostics DeprecatedNoEffect.java + */ +public class DeprecatedNoEffect { + void m1() { + @Deprecated int i1; // there should be a "has no effect" warning here + } + @Deprecated + void m2() { + @Deprecated int i2; // there should be a "has no effect" warning here also + } +} diff --git a/test/langtools/tools/javac/warnings/DeprecatedNoEffect.out b/test/langtools/tools/javac/warnings/DeprecatedNoEffect.out new file mode 100644 index 00000000000..55003e62fb8 --- /dev/null +++ b/test/langtools/tools/javac/warnings/DeprecatedNoEffect.out @@ -0,0 +1,3 @@ +DeprecatedNoEffect.java:8:21: compiler.warn.deprecated.annotation.has.no.effect: kindname.variable +DeprecatedNoEffect.java:12:21: compiler.warn.deprecated.annotation.has.no.effect: kindname.variable +2 warnings diff --git a/test/langtools/tools/lib/builder/AbstractBuilder.java b/test/langtools/tools/lib/builder/AbstractBuilder.java index e528fe60792..4717f456d08 100644 --- a/test/langtools/tools/lib/builder/AbstractBuilder.java +++ b/test/langtools/tools/lib/builder/AbstractBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -203,6 +203,13 @@ public abstract class AbstractBuilder { this.modifiers = modifiers; } + boolean isInterface() { + if (modifiers.isEmpty()) { + throw new IllegalStateException("modifiers not initialized"); + } + return modifiers.getLast().endsWith("interface"); + } + @Override public String toString() { OutputWriter ow = new OutputWriter(); diff --git a/test/langtools/tools/lib/builder/ClassBuilder.java b/test/langtools/tools/lib/builder/ClassBuilder.java index feafa77db56..2c57f0e0c13 100644 --- a/test/langtools/tools/lib/builder/ClassBuilder.java +++ b/test/langtools/tools/lib/builder/ClassBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -30,6 +30,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.ListIterator; import java.util.regex.Matcher; @@ -54,6 +55,7 @@ public class ClassBuilder extends AbstractBuilder { private String extendsType; private final List implementsTypes; + private final List permitsTypes; private final List members; private final List inners; private final List nested; @@ -86,6 +88,7 @@ public class ClassBuilder extends AbstractBuilder { } imports = new ArrayList<>(); implementsTypes = new ArrayList<>(); + permitsTypes = new ArrayList<>(); members = new ArrayList<>(); nested = new ArrayList<>(); inners = new ArrayList<>(); @@ -153,7 +156,11 @@ public class ClassBuilder extends AbstractBuilder { * @return this builder. */ public ClassBuilder setExtends(String name) { - extendsType = name; + if (modifiers.isInterface()) { + implementsTypes.add(name); + } else { + extendsType = name; + } return this; } @@ -163,7 +170,17 @@ public class ClassBuilder extends AbstractBuilder { * @return this builder. */ public ClassBuilder addImplements(String... names) { - implementsTypes.addAll(List.of(names)); + implementsTypes.addAll(Arrays.asList(names)); + return this; + } + + /** + * Adds a permits declaration(s). + * @param names the subtypes + * @return this builder + */ + public ClassBuilder addPermits(String... names) { + permitsTypes.addAll(Arrays.asList(names)); return this; } @@ -225,28 +242,25 @@ public class ClassBuilder extends AbstractBuilder { ow.println("// NO_API_COMMENT"); break; } + assert !modifiers.modifiers.isEmpty(); ow.print(modifiers.toString()); ow.print(clsname); if (typeParameter != null) { - ow.print(typeParameter + " "); - } else { - ow.print(" "); + ow.print(typeParameter); } if (extendsType != null && !extendsType.isEmpty()) { - ow.print("extends " + extendsType + " "); + assert !modifiers.isInterface(); + ow.print(" extends " + extendsType); } if (!implementsTypes.isEmpty()) { - ow.print("implements "); - - ListIterator iter = implementsTypes.listIterator(); - while (iter.hasNext()) { - String s = iter.next() ; - ow.print(s); - if (iter.hasNext()) - ow.print(", "); - } + ow.print(modifiers.isInterface() ? " extends " : " implements "); + ow.print(String.join(", ", implementsTypes)); } - ow.print("{"); + if (!permitsTypes.isEmpty()) { + ow.print(" permits "); + ow.print(String.join(", ", permitsTypes)); + } + ow.print(" {"); if (!nested.isEmpty()) { ow.println(""); nested.forEach(m -> ow.println(m.toString())); diff --git a/test/langtools/tools/lib/toolbox/ToolBox.java b/test/langtools/tools/lib/toolbox/ToolBox.java index ee217ab2c0c..1cb2fa6d3f9 100644 --- a/test/langtools/tools/lib/toolbox/ToolBox.java +++ b/test/langtools/tools/lib/toolbox/ToolBox.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, 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 @@ -666,6 +666,27 @@ public class ToolBox { return Path.of(testJDK, "bin", tool); } + /** + * Finds a file with a path relative to the langtools test root directory. + * + * @param path the desired path from test/langtools + * @return the file, if found + */ + public Path findFromTestRoot(String path) { + Path testSrc = Path.of(System.getProperty("test.src", ".")); + + for (Path d = testSrc; d != null; d = d.getParent()) { + if (Files.exists(d.resolve("TEST.ROOT"))) { + Path file = d.resolve(path); + if (Files.exists(file)) { + return file; + } + } + } + + return null; + } + /** * Returns a string representing the contents of an {@code Iterable} as a list. * diff --git a/test/lib/RedefineClassHelper.java b/test/lib/RedefineClassHelper.java index ce27fb33f44..064778b3a2a 100644 --- a/test/lib/RedefineClassHelper.java +++ b/test/lib/RedefineClassHelper.java @@ -107,7 +107,7 @@ public class RedefineClassHelper { * Main method to be invoked before test to create the redefineagent.jar */ public static void main(String[] args) throws Exception { - String manifest = "Premain-Class: RedefineClassHelper\nCan-Redefine-Classes: true\n"; + String manifest = "Premain-Class: RedefineClassHelper\nCan-Redefine-Classes: true\nCan-Retransform-Classes: true\n"; ClassFileInstaller.writeJar("redefineagent.jar", ClassFileInstaller.Manifest.fromString(manifest), "RedefineClassHelper"); } } diff --git a/test/lib/jdk/test/lib/Utils.java b/test/lib/jdk/test/lib/Utils.java index 2f46ed87340..95b7a117b2c 100644 --- a/test/lib/jdk/test/lib/Utils.java +++ b/test/lib/jdk/test/lib/Utils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, 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,6 +105,11 @@ public final class Utils { */ public static final String TEST_SRC = System.getProperty("test.src", "").trim(); + /** + * Returns the value of 'test.src.path' system property. + */ + public static final String TEST_SRC_PATH = System.getProperty("test.src.path", "").trim(); + /** * Returns the value of 'test.root' system property. */ diff --git a/test/lib/jdk/test/lib/cds/CDSAppTester.java b/test/lib/jdk/test/lib/cds/CDSAppTester.java index 18356dd8aa9..3eac8a35a37 100644 --- a/test/lib/jdk/test/lib/cds/CDSAppTester.java +++ b/test/lib/jdk/test/lib/cds/CDSAppTester.java @@ -480,11 +480,20 @@ abstract public class CDSAppTester { // See JEP 483 public void runAOTWorkflow(String... args) throws Exception { this.workflow = Workflow.AOT; - boolean oneStepTraining = true; // by default use onestep trainning + + // By default use twostep training -- tests are much easier to write this way, as + // the stdout/stderr of the training run is clearly separated from the assembly phase. + // + // Many older test cases written before JEP 514 were not aware of one step treaining + // and may not check the stdout/stderr correctly. + boolean oneStepTraining = false; if (System.getProperty("CDSAppTester.two.step.training") != null) { oneStepTraining = false; } + if (System.getProperty("CDSAppTester.one.step.training") != null) { + oneStepTraining = true; + } if (args.length > 1) { // Tests such as test/hotspot/jtreg/runtime/cds/appcds/aotCache/SpecialCacheNames.java diff --git a/test/lib/jdk/test/lib/cds/CDSTestUtils.java b/test/lib/jdk/test/lib/cds/CDSTestUtils.java index 1c51a693907..59e4a1bbbde 100644 --- a/test/lib/jdk/test/lib/cds/CDSTestUtils.java +++ b/test/lib/jdk/test/lib/cds/CDSTestUtils.java @@ -324,7 +324,7 @@ public class CDSTestUtils { public static void checkCommonExecExceptions(OutputAnalyzer output, Exception e) throws Exception { if (output.getStdout().contains("https://bugreport.java.com/bugreport/crash.jsp")) { - throw new RuntimeException("Hotspot crashed"); + throw new RuntimeException(getCrashMessage(output.getStdout())); } if (output.getStdout().contains("TEST FAILED")) { throw new RuntimeException("Test Failed"); diff --git a/test/lib/jdk/test/lib/jfr/EventNames.java b/test/lib/jdk/test/lib/jfr/EventNames.java index 8b0113f75f4..06ee62a2f7c 100644 --- a/test/lib/jdk/test/lib/jfr/EventNames.java +++ b/test/lib/jdk/test/lib/jfr/EventNames.java @@ -115,6 +115,7 @@ public class EventNames { public static final String ShenandoahHeapRegionInformation = PREFIX + "ShenandoahHeapRegionInformation"; public static final String ShenandoahHeapRegionStateChange = PREFIX + "ShenandoahHeapRegionStateChange"; public static final String ShenandoahEvacuationInformation = PREFIX + "ShenandoahEvacuationInformation"; + public static final String ShenandoahPromotionInformation = PREFIX + "ShenandoahPromotionInformation"; public static final String TenuringDistribution = PREFIX + "TenuringDistribution"; public static final String GarbageCollection = PREFIX + "GarbageCollection"; public static final String ParallelOldGarbageCollection = PREFIX + "ParallelOldGarbageCollection"; diff --git a/test/lib/jdk/test/lib/jfr/Events.java b/test/lib/jdk/test/lib/jfr/Events.java index 8bbf22ca63a..03e1e39cfe3 100644 --- a/test/lib/jdk/test/lib/jfr/Events.java +++ b/test/lib/jdk/test/lib/jfr/Events.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, 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 @@ -33,6 +33,7 @@ import java.io.IOException; import java.nio.file.Path; import java.time.Duration; import java.time.Instant; +import java.util.Comparator; import java.util.List; import jdk.jfr.AnnotationElement; @@ -280,6 +281,20 @@ public class Events { return RecordingFile.readAllEvents(makeCopy(recording)); } + /** + * Creates a list of events from a recording, ordered by the end time. + * + * @param recording recording, not {@code null} + * @return a list, not null + * @throws IOException if an event set could not be created due to I/O + * errors. + */ + public static List fromRecordingOrdered(Recording recording) throws IOException { + List events = fromRecording(recording); + events.sort(Comparator.comparing(RecordedEvent::getEndTime)); + return events; + } + public static RecordingFile copyTo(Recording r) throws IOException { return new RecordingFile(makeCopy(r)); } diff --git a/test/lib/jdk/test/lib/json/JSONValue.java b/test/lib/jdk/test/lib/json/JSONValue.java index f89d13b3bba..3ac6441b4c0 100644 --- a/test/lib/jdk/test/lib/json/JSONValue.java +++ b/test/lib/jdk/test/lib/json/JSONValue.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -25,46 +25,55 @@ package jdk.test.lib.json; import java.math.BigInteger; import java.util.ArrayList; import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.Optional; -public interface JSONValue { +public sealed interface JSONValue + permits JSONValue.JSONObject, JSONValue.JSONArray, JSONValue.JSONString, + JSONValue.JSONNumber, JSONValue.JSONBoolean, JSONValue.JSONNull { public final class JSONObject implements JSONValue { - private final Map value; + private final Map members; - public JSONObject(Map value) { - this.value = value; + private JSONObject(Map members) { + this.members = Map.copyOf(members); } @Override - public JSONObject asObject() { - return this; - } - - public JSONValue get(String k) { - return value.get(k); + public JSONValue get(String name) { + JSONValue v = members.get(name); + if (v == null) { + throw new NoSuchElementException("member " + name + " does not exist"); + } + return v; } @Override - public int size() { - return value.size(); + public Optional getOrAbsent(String name) { + return Optional.ofNullable(members.get(name)); + } + + @Override + public Map members() { + return members; } @Override public String toString() { var builder = new StringBuilder(); builder.append("{"); - for (var key : value.keySet()) { + for (String key : members.keySet()) { builder.append("\""); builder.append(key); builder.append("\":"); - builder.append(value.get(key).toString()); + builder.append(members.get(key).toString()); builder.append(","); } - var end = builder.length() - 1; + int end = builder.length() - 1; if (builder.charAt(end) == ',') { builder.deleteCharAt(end); } @@ -72,13 +81,17 @@ public interface JSONValue { builder.append("}"); return builder.toString(); } + + public static JSONObject of(Map members) { + return new JSONObject(members); + } } public final class JSONString implements JSONValue { private final String value; - public JSONString(String value) { - this.value = value; + private JSONString(String value) { + this.value = Objects.requireNonNull(value); } @Override @@ -88,9 +101,6 @@ public interface JSONValue { @Override public String toString() { - if (value == null) { - return "null"; - } var builder = new StringBuilder(); builder.append("\""); @@ -131,26 +141,17 @@ public interface JSONValue { builder.append("\""); return builder.toString(); } + + public static JSONString of(String value) { + return new JSONString(value); + } } - public final class JSONArray implements JSONValue, Iterable { - private final List values; + public final class JSONArray implements JSONValue { + private final List elements; - public JSONArray(List array) { - this.values = array; - } - - @Override - public JSONArray asArray() { - return this; - } - - public JSONValue get(int i) { - return values.get(i); - } - - public int size() { - return values.size(); + JSONArray(List elements) { + this.elements = List.copyOf(elements); } @Override @@ -158,9 +159,10 @@ public interface JSONValue { var builder = new StringBuilder(); builder.append("["); - for (var i = 0; i < size(); i++) { - builder.append(get(i).toString()); - if (i != (size() - 1)) { + int size = elements.size(); + for (int i = 0; i < size; i++) { + builder.append(elements.get(i).toString()); + if (i != (size - 1)) { builder.append(","); } } @@ -169,8 +171,106 @@ public interface JSONValue { } @Override - public Iterator iterator() { - return values.iterator(); + public List elements() { + return elements; + } + + @Override + public JSONValue element(int index) { + return elements.get(index); + } + + public static JSONArray of(List elements) { + return new JSONArray(elements); + } + } + + public final class JSONNumber implements JSONValue { + private final String value; + + private JSONNumber(String value) { + this.value = Objects.requireNonNull(value); + } + + @Override + public int asInt() { + return Integer.parseInt(value); + } + + @Override + public long asLong() { + return Long.parseLong(value); + } + + @Override + public double asDouble() { + return Double.parseDouble(value); + } + + @Override + public String toString() { + return value; + } + + public static JSONNumber of(String value) { + return new JSONNumber(value); + } + + public static JSONNumber of(int value) { + return of(String.valueOf(value)); + } + + public static JSONNumber of(long value) { + return of(String.valueOf(value)); + } + + public static JSONNumber of(double value) { + return of(String.valueOf(value)); + } + } + + public final class JSONBoolean implements JSONValue { + private static JSONBoolean TRUE = new JSONBoolean(true); + private static JSONBoolean FALSE = new JSONBoolean(false); + + private final boolean value; + + private JSONBoolean(boolean value) { + this.value = value; + } + + @Override + public boolean asBoolean() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + public static JSONBoolean of(boolean value) { + return value ? TRUE : FALSE; + } + } + + public final class JSONNull implements JSONValue { + private static JSONNull NULL = new JSONNull(); + + private JSONNull() {} + + @Override + public Optional valueOrNull() { + return Optional.empty(); + } + + @Override + public String toString() { + return "null"; + } + + public static JSONNull of() { + return NULL; } } @@ -181,8 +281,8 @@ public interface JSONValue { JSONParser() { } - private IllegalStateException failure(String message) { - return new IllegalStateException(String.format("[%d]: %s : %s", pos, message, input)); + private IllegalArgumentException failure(String message) { + return new IllegalArgumentException(String.format("[%d]: %s : %s", pos, message, input)); } private char current() { @@ -220,13 +320,13 @@ public interface JSONValue { } } - private JSONString parseBoolean() { + private JSONBoolean parseBoolean() { if (current() == 't') { expect('r'); expect('u'); expect('e'); advance(); - return new JSONString("true"); + return JSONBoolean.of(true); } if (current() == 'f') { @@ -235,7 +335,7 @@ public interface JSONValue { expect('s'); expect('e'); advance(); - return new JSONString("false"); + return JSONBoolean.of(false); } throw failure("a boolean can only be 'true' or 'false'"); } @@ -319,11 +419,10 @@ public interface JSONValue { var value = builder.toString(); if (isInteger) { new BigInteger(value); - return new JSONString(value); } else { Double.parseDouble(value); - return new JSONString(value); } + return JSONNumber.of(value); } private JSONString parseString() { @@ -374,7 +473,7 @@ public interface JSONValue { } advance(); // step beyond closing " - return new JSONString(builder.toString()); + return JSONString.of(builder.toString()); } private JSONArray parseArray() { @@ -397,15 +496,15 @@ public interface JSONValue { } advance(); // step beyond closing ']' - return new JSONArray(list); + return JSONArray.of(list); } - public JSONString parseNull() { + public JSONNull parseNull() { expect('u'); expect('l'); expect('l'); advance(); - return new JSONString(null); + return JSONNull.of(); } public JSONObject parseObject() { @@ -438,7 +537,7 @@ public interface JSONValue { } advance(); // step beyond '}' - return new JSONObject(map); + return JSONObject.of(map); } private boolean isDigit(char c) { @@ -526,27 +625,51 @@ public interface JSONValue { } } - public static JSONValue parse(String s) { + static JSONValue parse(String s) { return new JSONParser().parse(s); } - default int size() { - throw new IllegalStateException("Size operation unsupported"); + default JSONValue get(String name) { + throw new UnsupportedOperationException("Unsupported conversion to object"); + } + + default Optional getOrAbsent(String name) { + throw new UnsupportedOperationException("Unsupported conversion to object"); + } + + default Optional valueOrNull() { + return Optional.of(this); + } + + default Map members() { + throw new UnsupportedOperationException("Unsupported conversion to object"); + } + + default List elements() { + throw new UnsupportedOperationException("Unsupported conversion to array"); + } + + default JSONValue element(int index) { + throw new UnsupportedOperationException("Unsupported conversion to array"); } default String asString() { - throw new IllegalStateException("Unsupported conversion to String"); + throw new UnsupportedOperationException("Unsupported conversion to string"); } - default JSONArray asArray() { - throw new IllegalStateException("Unsupported conversion to array"); + default int asInt() { + throw new UnsupportedOperationException("Unsupported conversion to number"); } - default JSONObject asObject() { - throw new IllegalStateException("Unsupported conversion to object"); + default long asLong() { + throw new UnsupportedOperationException("Unsupported conversion to number"); } - default JSONValue get(String field) { - return asObject().get(field); + default double asDouble() { + throw new UnsupportedOperationException("Unsupported conversion to number"); + } + + default boolean asBoolean() { + throw new UnsupportedOperationException("Unsupported conversion to boolean"); } } diff --git a/test/lib/jdk/test/lib/net/IPSupport.java b/test/lib/jdk/test/lib/net/IPSupport.java index 31255e20c6a..4a77c3a9bae 100644 --- a/test/lib/jdk/test/lib/net/IPSupport.java +++ b/test/lib/jdk/test/lib/net/IPSupport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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,6 +34,7 @@ import java.net.InetAddress; import java.net.ProtocolFamily; import java.net.StandardProtocolFamily; import java.nio.channels.SocketChannel; +import java.util.Optional; import jtreg.SkippedException; @@ -124,13 +125,33 @@ public class IPSupport { * is non-operational */ public static void throwSkippedExceptionIfNonOperational() throws SkippedException { + Optional configurationIssue = diagnoseConfigurationIssue(); + configurationIssue.map(SkippedException::new).ifPresent(x -> { + throw x; + }); + } + + /** + * Checks that the platform supports the ability to create a + * minimally-operational socket whose protocol is either one of IPv4 + * or IPv6. + * + *

    A minimally-operation socket is one that can be created and + * bound to an IP-specific loopback address. IP support is + * considered non-operational if a socket cannot be bound to either + * one of, an IPv4 loopback address, or the IPv6 loopback address. + * + * @return Optinal with config issue or empty Optinal if no issue found + */ + public static Optional diagnoseConfigurationIssue(){ if (!currentConfigurationIsValid()) { ByteArrayOutputStream os = new ByteArrayOutputStream(); PrintStream ps = new PrintStream(os); ps.println("Invalid networking configuration"); printPlatformSupport(ps); - throw new SkippedException(os.toString()); + return Optional.of(os.toString()); } + return Optional.empty(); } /** diff --git a/test/lib/jdk/test/lib/security/SecurityUtils.java b/test/lib/jdk/test/lib/security/SecurityUtils.java index be6ff1cc0e3..78dc2aa2729 100644 --- a/test/lib/jdk/test/lib/security/SecurityUtils.java +++ b/test/lib/jdk/test/lib/security/SecurityUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -23,8 +23,10 @@ package jdk.test.lib.security; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; +import java.io.PrintStream; import java.nio.ByteBuffer; import java.security.KeyStore; import java.security.Security; @@ -219,5 +221,40 @@ public final class SecurityUtils { return ((m.get() & 0xFF) << 8) | (m.get() & 0xFF); } + // Helper method to run and get log. + public static String runAndGetLog(Runnable runnable) { + System.setProperty("javax.net.debug", "ssl"); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + PrintStream err = new PrintStream(baos); + PrintStream origErr = System.err; + System.setErr(err); + + runnable.run(); + err.close(); + + // Save the log output and then print it as usual. + String log = baos.toString(); + System.setErr(origErr); + System.err.print(log); + return log; + } + + // Helper method to find log messages. + public static int countSubstringOccurrences(String str, String sub) { + if (str == null || sub == null || sub.isEmpty()) { + return 0; + } + + int count = 0; + int lastIndex = 0; + + while ((lastIndex = str.indexOf(sub, lastIndex)) != -1) { + count++; + lastIndex += sub.length(); + } + + return count; + } + private SecurityUtils() {} } diff --git a/test/lib/jdk/test/lib/thread/ThreadWrapper.java b/test/lib/jdk/test/lib/thread/ThreadWrapper.java new file mode 100644 index 00000000000..ab850bf5a4a --- /dev/null +++ b/test/lib/jdk/test/lib/thread/ThreadWrapper.java @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2026, 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. + */ + + +package jdk.test.lib.thread; + +import java.time.Duration; +import java.util.Map; + +/* + The ThreadWrapper is a helper class that allows to extend + coverage of virtual threads testing for existing tests with threads. + + Specifically, it is useful for the pattern where Thread is extended + by some class. Example: + + class resumethrd02Thread extends Thread {...} + ... + resumethrd02Thread thr = new resumethrd02Thread(); + + The test can be updated to use this wrapper: + class resumethrd02Thread extends ThreadWrapper {...} + ... + resumethrd02Thread thr = new resumethrd02Thread(); + + So resumethrd02Thread can be run with platform or virtual threads. + + Method getThread() is used to get instance of Thread. + + It is not expected to use this wrapper for new tests or classes that + are not extending Thread. The TestThreadFactory should be used to + create threads in such cases. + */ + +public class ThreadWrapper implements Runnable { + private final Thread thread; + + @SuppressWarnings("this-escape") + public ThreadWrapper() { + // thread is a platform or virtual thread + thread = TestThreadFactory.newThread(this); + } + + @SuppressWarnings("this-escape") + public ThreadWrapper(String name) { + // thread is a platform or virtual thread + thread = TestThreadFactory.newThread(this, name); + } + + public Thread getThread() { + return thread; + } + + public static Thread currentThread() { + return Thread.currentThread(); + } + + public static void yield() { + Thread.yield(); + } + + public static void sleep(long millis) throws InterruptedException { + Thread.sleep(millis); + } + + public static void sleep(long millis, int nanos) throws InterruptedException { + Thread.sleep(millis, nanos); + } + + public static void sleep(Duration duration) throws InterruptedException { + Thread.sleep(duration); + } + + public static void onSpinWait() { + Thread.onSpinWait(); + } + + public static Thread.Builder.OfPlatform ofPlatform() { + return Thread.ofPlatform(); + } + + public static Thread.Builder.OfVirtual ofVirtual() { + return Thread.ofVirtual(); + } + + public static Thread startVirtualThread(Runnable task) { + return Thread.startVirtualThread(task); + } + + public boolean isVirtual() { + return thread.isVirtual(); + } + + public void start() { + thread.start(); + } + + public void run() { + } + + public void interrupt() { + thread.interrupt(); + } + + public static boolean interrupted() { + return Thread.interrupted(); + } + + public boolean isInterrupted() { + return thread.isInterrupted(); + } + + public boolean isAlive() { + return thread.isAlive(); + } + + public void setPriority(int newPriority) { + thread.setPriority(newPriority); + } + + public int getPriority() { + return thread.getPriority(); + } + + public void setName(String name) { + thread.setName(name); + } + + public String getName() { + return thread.getName(); + } + + public ThreadGroup getThreadGroup() { + return thread.getThreadGroup(); + } + + public static int activeCount() { + return Thread.activeCount(); + } + + public static int enumerate(Thread[] tarray) { + return Thread.enumerate(tarray); + } + + public void join(long millis) throws InterruptedException { + thread.join(millis); + } + + public void join(long millis, int nanos) throws InterruptedException { + thread.join(millis, nanos); + } + + public void join() throws InterruptedException { + thread.join(); + } + + public boolean join(Duration duration) throws InterruptedException { + return thread.join(duration); + } + + public static void dumpStack() { + Thread.dumpStack(); + } + + public void setDaemon(boolean on) { + thread.setDaemon(on); + } + + public boolean isDaemon() { + return thread.isDaemon(); + } + + @Override + public String toString() { + return thread.toString(); + } + + public ClassLoader getContextClassLoader() { + return thread.getContextClassLoader(); + } + + public void setContextClassLoader(ClassLoader cl) { + thread.setContextClassLoader(cl); + } + + public static boolean holdsLock(Object obj) { + return Thread.holdsLock(obj); + } + + public StackTraceElement[] getStackTrace() { + return thread.getStackTrace(); + } + + public static Map getAllStackTraces() { + return Thread.getAllStackTraces(); + } + + @Deprecated(since = "19") + public long getId() { + return thread.getId(); + } + + public long threadId() { + return thread.threadId(); + } + + public Thread.State getState() { + return thread.getState(); + } + + public static void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler ueh) { + Thread.setDefaultUncaughtExceptionHandler(ueh); + } + + public static Thread.UncaughtExceptionHandler getDefaultUncaughtExceptionHandler() { + return Thread.getDefaultUncaughtExceptionHandler(); + } + + public Thread.UncaughtExceptionHandler getUncaughtExceptionHandler() { + return thread.getUncaughtExceptionHandler(); + } + + public void setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler ueh) { + thread.setUncaughtExceptionHandler(ueh); + } +} diff --git a/test/lib/jdk/test/lib/threaddump/ThreadDump.java b/test/lib/jdk/test/lib/threaddump/ThreadDump.java index ca728e625fc..77dd3dd8c03 100644 --- a/test/lib/jdk/test/lib/threaddump/ThreadDump.java +++ b/test/lib/jdk/test/lib/threaddump/ThreadDump.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, 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 @@ -32,13 +32,17 @@ import java.util.Map; import java.util.Optional; import java.util.OptionalLong; import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; import java.util.stream.Stream; import jdk.test.lib.json.JSONValue; /** * Represents a thread dump that is obtained by parsing JSON text. A thread dump in JSON * format is generated with the {@code com.sun.management.HotSpotDiagnosticMXBean} API or - * using {@code jcmd Thread.dump_to_file -format=json }. + * using {@code jcmd Thread.dump_to_file -format=json }. The thread dump + * format is documented in {@code + * src/jdk.management/share/classes/com/sun/management/doc-files/threadDump.schema.json}. * *

    The following is an example thread dump that is parsed by this class. Many of the * objects are collapsed to reduce the size. @@ -46,9 +50,10 @@ import jdk.test.lib.json.JSONValue; *

    {@code
      * {
      *   "threadDump": {
    - *     "processId": "63406",
    - *     "time": "2022-05-20T07:37:16.308017Z",
    - *     "runtimeVersion": "19",
    + *     "formatVersion": 2,
    + *     "processId": 63406,
    + *     "time": "2026-03-25T09:20:08.591503Z",
    + *     "runtimeVersion": "27",
      *     "threadContainers": [
      *       {
      *         "container": "",
    @@ -56,12 +61,12 @@ import jdk.test.lib.json.JSONValue;
      *         "owner": null,
      *         "threads": [
      *          {
    - *            "tid": "1",
    + *            "tid": 1,
      *            "name": "main",
      *            "stack": [...]
      *          },
      *          {
    - *            "tid": "8",
    + *            "tid": 8,
      *            "name": "Reference Handler",
      *            "state": "RUNNABLE",
      *            "stack": [
    @@ -76,21 +81,21 @@ import jdk.test.lib.json.JSONValue;
      *          {"name": "Monitor Ctrl-Break"...},
      *          {"name": "Notification Thread"...}
      *         ],
    - *         "threadCount": "7"
    + *         "threadCount": 7
      *       },
      *       {
      *         "container": "ForkJoinPool.commonPool\/jdk.internal.vm.SharedThreadContainer@56aac163",
      *         "parent": "",
      *         "owner": null,
      *         "threads": [...],
    - *         "threadCount": "1"
    + *         "threadCount": 1
      *       },
      *       {
      *         "container": "java.util.concurrent.ThreadPoolExecutor@20322d26\/jdk.internal.vm.SharedThreadContainer@184f6be2",
      *         "parent": "",
      *         "owner": null,
      *         "threads": [...],
    - *         "threadCount": "1"
    + *         "threadCount": 1
      *       }
      *     ]
      *   }
    @@ -149,14 +154,6 @@ public final class ThreadDump {
                 children.add(container);
             }
     
    -        /**
    -         * Returns the value of a property of this thread container, as a string.
    -         */
    -        private String getStringProperty(String propertyName) {
    -            JSONValue value = containerObj.get(propertyName);
    -            return (value != null) ? value.asString() : null;
    -        }
    -
             /**
              * Returns the thread container name.
              */
    @@ -168,10 +165,10 @@ public final class ThreadDump {
              * Return the thread identifier of the owner or empty OptionalLong if not owned.
              */
             public OptionalLong owner() {
    -            String owner = getStringProperty("owner");
    -            return (owner != null)
    -                    ? OptionalLong.of(Long.parseLong(owner))
    -                    : OptionalLong.empty();
    +            return containerObj.get("owner")  // number or null
    +                    .valueOrNull()
    +                    .map(v -> OptionalLong.of(v.asLong()))
    +                    .orElse(OptionalLong.empty());
             }
     
             /**
    @@ -192,12 +189,10 @@ public final class ThreadDump {
              * Returns a stream of {@code ThreadInfo} objects for the threads in this container.
              */
             public Stream threads() {
    -            JSONValue.JSONArray threadsObj = containerObj.get("threads").asArray();
    -            Set threadInfos = new HashSet<>();
    -            for (JSONValue threadObj : threadsObj) {
    -                threadInfos.add(new ThreadInfo(threadObj));
    -            }
    -            return threadInfos.stream();
    +            return containerObj.get("threads")
    +                    .elements()
    +                    .stream()
    +                    .map(ThreadInfo::new);
             }
     
             /**
    @@ -237,29 +232,10 @@ public final class ThreadDump {
             private final JSONValue threadObj;
     
             ThreadInfo(JSONValue threadObj) {
    -            this.tid = Long.parseLong(threadObj.get("tid").asString());
    +            this.tid = threadObj.get("tid").asLong();
                 this.threadObj = threadObj;
             }
     
    -        /**
    -         * Returns the value of a property of this thread object, as a string.
    -         */
    -        private String getStringProperty(String propertyName) {
    -            JSONValue value = threadObj.get(propertyName);
    -            return (value != null) ? value.asString() : null;
    -        }
    -
    -        /**
    -         * Returns the value of a property of an object in this thread object, as a string.
    -         */
    -        private String getStringProperty(String objectName, String propertyName) {
    -            if (threadObj.get(objectName) instanceof JSONValue.JSONObject obj
    -                    && obj.get(propertyName) instanceof JSONValue value) {
    -                return value.asString();
    -            }
    -            return null;
    -        }
    -
             /**
              * Returns the thread identifier.
              */
    @@ -271,83 +247,92 @@ public final class ThreadDump {
              * Returns the thread name.
              */
             public String name() {
    -            return getStringProperty("name");
    +            return threadObj.get("name").asString();
             }
     
             /**
              * Returns the thread state.
              */
             public String state() {
    -            return getStringProperty("state");
    +            return threadObj.get("state").asString();
             }
     
             /**
              * Returns true if virtual thread.
              */
             public boolean isVirtual() {
    -            String s = getStringProperty("virtual");
    -            return (s != null) ? Boolean.parseBoolean(s) : false;
    +            return threadObj.getOrAbsent("virtual")
    +                    .map(JSONValue::asBoolean)
    +                    .orElse(false);
             }
     
             /**
    -         * Returns the thread's parkBlocker.
    +         * Returns the thread's parkBlocker or null.
              */
             public String parkBlocker() {
    -            return getStringProperty("parkBlocker", "object");
    +            return threadObj.getOrAbsent("parkBlocker")
    +                    .map(v -> v.get("object").asString())
    +                    .orElse(null);
             }
     
             /**
              * Returns the owner of the parkBlocker if the parkBlocker is an AbstractOwnableSynchronizer.
              */
             public OptionalLong parkBlockerOwner() {
    -            String owner = getStringProperty("parkBlocker", "owner");
    -            return (owner != null)
    -                    ? OptionalLong.of(Long.parseLong(owner))
    -                    : OptionalLong.empty();
    +            return threadObj.getOrAbsent("parkBlocker")
    +                    .map(v -> OptionalLong.of(v.get("owner").asLong()))
    +                    .orElse(OptionalLong.empty());
             }
     
             /**
    -         * Returns the object that the thread is blocked entering its monitor.
    +         * Returns the object that the thread is blocked entering its monitor or null.
              */
             public String blockedOn() {
    -            return getStringProperty("blockedOn");
    +            return threadObj.getOrAbsent("blockedOn")
    +                    .map(JSONValue::asString)
    +                    .orElse(null);
             }
     
             /**
    -         * Return the object that is the therad is waiting on with Object.wait.
    +         * Return the object that is the thread is waiting on with Object.wait or null.
              */
             public String waitingOn() {
    -            return getStringProperty("waitingOn");
    +            return threadObj.getOrAbsent("waitingOn")
    +                    .map(JSONValue::asString)
    +                    .orElse(null);
             }
     
             /**
              * Returns the thread stack.
              */
             public Stream stack() {
    -            JSONValue.JSONArray stackObj = threadObj.get("stack").asArray();
    -            List stack = new ArrayList<>();
    -            for (JSONValue steObject : stackObj) {
    -                stack.add(steObject.asString());
    -            }
    -            return stack.stream();
    +            return threadObj.get("stack")
    +                    .elements()
    +                    .stream()
    +                    .map(JSONValue::asString);
             }
     
             /**
              * Return a map of monitors owned.
              */
             public Map> ownedMonitors() {
    -            Map> ownedMonitors = new HashMap<>();
    -            JSONValue monitorsOwnedObj = threadObj.get("monitorsOwned");
    -            if (monitorsOwnedObj != null) {
    -                for (JSONValue obj : monitorsOwnedObj.asArray()) {
    -                    int depth = Integer.parseInt(obj.get("depth").asString());
    -                    for (JSONValue lock : obj.get("locks").asArray()) {
    -                        ownedMonitors.computeIfAbsent(depth, _ -> new ArrayList<>())
    -                                .add(lock.asString());
    -                    }
    -                }
    -            }
    -            return ownedMonitors;
    +            Map> result = new HashMap<>();
    +            threadObj.getOrAbsent("monitorsOwned")
    +                    .map(JSONValue::elements)
    +                    .orElse(List.of())
    +                    .forEach(e -> {
    +                        int depth = e.get("depth").asInt();
    +                        List locks = e.get("locks")
    +                                .elements()
    +                                .stream()
    +                                .map(v -> v.valueOrNull()  // string or null
    +                                        .map(JSONValue::asString)
    +                                        .orElse(null))
    +                                .toList();
    +                        result.computeIfAbsent(depth, _ -> new ArrayList<>()).addAll(locks);
    +                    });
    +
    +            return result;
             }
     
             /**
    @@ -355,10 +340,9 @@ public final class ThreadDump {
              * its carrier.
              */
             public OptionalLong carrier() {
    -            String s = getStringProperty("carrier");
    -            return (s != null)
    -                    ? OptionalLong.of(Long.parseLong(s))
    -                    : OptionalLong.empty();
    +            return threadObj.getOrAbsent("carrier")
    +                    .map(v -> OptionalLong.of(v.asLong()))
    +                    .orElse(OptionalLong.empty());
             }
     
             @Override
    @@ -388,33 +372,24 @@ public final class ThreadDump {
             }
         }
     
    -    /**
    -     * Returns the value of a property of this thread dump, as a string.
    -     */
    -    private String getStringProperty(String propertyName) {
    -        JSONValue value = threadDumpObj.get(propertyName);
    -        return (value != null) ? value.asString() : null;
    -    }
    -
         /**
          * Returns the value of threadDump/processId.
          */
         public long processId() {
    -        return Long.parseLong(getStringProperty("processId"));
    -    }
    +        return threadDumpObj.get("processId").asLong(); }
     
         /**
          * Returns the value of threadDump/time.
          */
         public String time() {
    -        return getStringProperty("time");
    +        return threadDumpObj.get("time").asString();
         }
     
         /**
          * Returns the value of threadDump/runtimeVersion.
          */
         public String runtimeVersion() {
    -        return getStringProperty("runtimeVersion");
    +        return threadDumpObj.get("runtimeVersion").asString();
         }
     
         /**
    @@ -447,35 +422,53 @@ public final class ThreadDump {
          */
         public static ThreadDump parse(String json) {
             JSONValue threadDumpObj = JSONValue.parse(json).get("threadDump");
    +        int formatVersion = threadDumpObj.get("formatVersion").asInt();
    +        if (formatVersion != 2) {
    +            fail("Format " + formatVersion + " not supported");
    +        }
     
             // threadContainers array, preserve insertion order (parents are added before children)
    -        Map containerObjs = new LinkedHashMap<>();
    -        JSONValue threadContainersObj = threadDumpObj.get("threadContainers");
    -        for (JSONValue containerObj : threadContainersObj.asArray()) {
    -            String name = containerObj.get("container").asString();
    -            containerObjs.put(name, containerObj);
    -        }
    +        Map containerObjs = threadDumpObj.get("threadContainers")
    +                .elements()
    +                .stream()
    +                .collect(Collectors.toMap(
    +                        c -> c.get("container").asString(),
    +                        Function.identity(),
    +                        (a, b) -> { fail("Duplicate container"); return null; },
    +                        LinkedHashMap::new
    +                ));
     
             // find root and create tree of thread containers
             ThreadContainer root = null;
             Map map = new HashMap<>();
             for (String name : containerObjs.keySet()) {
                 JSONValue containerObj = containerObjs.get(name);
    -            String parentName = containerObj.get("parent").asString();
    -            if (parentName == null) {
    +            JSONValue parentObj = containerObj.get("parent");
    +            if (parentObj instanceof JSONValue.JSONNull) {
    +                if (root != null) {
    +                    fail("More than one root container");
    +                }
                     root = new ThreadContainer(name, null, containerObj);
                     map.put(name, root);
                 } else {
    -                var parent = map.get(parentName);
    +                String parentName = parentObj.asString();
    +                ThreadContainer parent = map.get(parentName);
                     if (parent == null) {
    -                    throw new RuntimeException("Thread container " + name + " found before " + parentName);
    +                    fail("Thread container " + name + " found before " + parentName);
                     }
                     var container = new ThreadContainer(name, parent, containerObj);
                     parent.addChild(container);
                     map.put(name, container);
                 }
             }
    +        if (root == null) {
    +            fail("No root container");
    +        }
     
             return new ThreadDump(root, map, threadDumpObj);
         }
    -}
    \ No newline at end of file
    +
    +    private static void fail(String message) {
    +        throw new RuntimeException(message);
    +    }
    +}
    diff --git a/test/lib/jdk/test/whitebox/code/BlobType.java b/test/lib/jdk/test/whitebox/code/BlobType.java
    index a2290acc7b6..e2d57f79484 100644
    --- a/test/lib/jdk/test/whitebox/code/BlobType.java
    +++ b/test/lib/jdk/test/whitebox/code/BlobType.java
    @@ -46,8 +46,16 @@ public enum BlobType {
                         || type == BlobType.MethodNonProfiled;
             }
         },
    +    MethodHot(2, "CodeHeap 'hot nmethods'", "HotCodeHeapSize") {
    +        @Override
    +        public boolean allowTypeWhenOverflow(BlobType type) {
    +            return super.allowTypeWhenOverflow(type)
    +                    || type == BlobType.MethodNonProfiled
    +                    || type == BlobType.MethodProfiled;
    +        }
    +    },
         // Non-nmethods like Buffers, Adapters and Runtime Stubs
    -    NonNMethod(2, "CodeHeap 'non-nmethods'", "NonNMethodCodeHeapSize") {
    +    NonNMethod(3, "CodeHeap 'non-nmethods'", "NonNMethodCodeHeapSize") {
             @Override
             public boolean allowTypeWhenOverflow(BlobType type) {
                 return super.allowTypeWhenOverflow(type)
    @@ -56,7 +64,7 @@ public enum BlobType {
             }
         },
         // All types (No code cache segmentation)
    -    All(3, "CodeCache", "ReservedCodeCacheSize");
    +    All(4, "CodeCache", "ReservedCodeCacheSize");
     
         public final int id;
         public final String sizeOptionName;
    @@ -99,6 +107,10 @@ public enum BlobType {
                 // there is no MethodProfiled in non tiered world or pure C1
                 result.remove(MethodProfiled);
             }
    +
    +        if (Long.valueOf(0).equals(whiteBox.getVMFlag("HotCodeHeapSize"))) {
    +            result.remove(MethodHot);
    +        }
             return result;
         }
     
    diff --git a/test/micro/org/openjdk/bench/java/nio/CharsetCanEncode.java b/test/micro/org/openjdk/bench/java/nio/CharsetCanEncode.java
    index ebfbc217a95..8c08a876696 100644
    --- a/test/micro/org/openjdk/bench/java/nio/CharsetCanEncode.java
    +++ b/test/micro/org/openjdk/bench/java/nio/CharsetCanEncode.java
    @@ -65,6 +65,9 @@ public class CharsetCanEncode {
         // sun.nio.cs.UTF_16LE
         private CharsetEncoder utf16le = Charset.forName("UTF-16LE").newEncoder();
     
    +    // sun.nio.cs.UTF_32LE
    +    private CharsetEncoder utf32le = Charset.forName("UTF-32LE").newEncoder();
    +
         @Benchmark
         public boolean asciiCanEncodeCharYes() {
             return ascii.canEncode('D');
    @@ -184,4 +187,24 @@ public class CharsetCanEncode {
         public boolean utf16leCanEncodeStringNo() {
             return utf16le.canEncode(String.valueOf(Character.MIN_SURROGATE));
         }
    +
    +    @Benchmark
    +    public boolean utf32leCanEncodeCharYes() {
    +        return utf32le.canEncode('D');
    +    }
    +
    +    @Benchmark
    +    public boolean utf32leCanEncodeStringYes() {
    +        return utf32le.canEncode("D");
    +    }
    +
    +    @Benchmark
    +    public boolean utf32leCanEncodeCharNo() {
    +        return utf32le.canEncode(Character.MIN_SURROGATE);
    +    }
    +
    +    @Benchmark
    +    public boolean utf32leCanEncodeStringNo() {
    +        return utf32le.canEncode(String.valueOf(Character.MIN_SURROGATE));
    +    }
     }
    diff --git a/test/micro/org/openjdk/bench/java/nio/file/FilesCopy.java b/test/micro/org/openjdk/bench/java/nio/file/FilesCopy.java
    new file mode 100644
    index 00000000000..9472920f071
    --- /dev/null
    +++ b/test/micro/org/openjdk/bench/java/nio/file/FilesCopy.java
    @@ -0,0 +1,68 @@
    +/*
    + * Copyright (c) 2026, 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.
    + */
    +
    +package org.openjdk.bench.java.nio.file;
    +
    +import java.io.IOException;
    +import java.nio.ByteBuffer;
    +import java.nio.channels.FileChannel;
    +import java.nio.file.Files;
    +import java.nio.file.Path;
    +import static java.nio.file.StandardOpenOption.*;
    +
    +import org.openjdk.jmh.annotations.Benchmark;
    +import org.openjdk.jmh.annotations.Scope;
    +import org.openjdk.jmh.annotations.Setup;
    +import org.openjdk.jmh.annotations.State;
    +import org.openjdk.jmh.annotations.TearDown;
    +
    +@State(Scope.Benchmark)
    +public class FilesCopy {
    +
    +    private static final int SIZE = Integer.MAX_VALUE;
    +    private static final Path FILE = Path.of("file.dat");
    +    private static final Path COPY = Path.of("copy.dat");
    +
    +    @Setup
    +    public void init() throws IOException {
    +        Files.deleteIfExists(FILE);
    +        Files.deleteIfExists(COPY);
    +        try (FileChannel fc = FileChannel.open(FILE, CREATE_NEW, READ, WRITE)) {
    +            fc.position(SIZE);
    +            fc.write(ByteBuffer.wrap(new byte[] {(byte)27}));
    +        }
    +    }
    +
    +    @TearDown
    +    public void cleanup() throws IOException {
    +        Files.deleteIfExists(FILE);
    +        Files.deleteIfExists(COPY);
    +    }
    +
    +    @Benchmark
    +    public void copyFile() throws IOException {
    +        Files.copy(FILE, COPY);
    +        Files.delete(COPY);
    +    }
    +
    +}
    diff --git a/test/micro/org/openjdk/bench/jdk/incubator/vector/Float16OperationsBenchmark.java b/test/micro/org/openjdk/bench/jdk/incubator/vector/Float16OperationsBenchmark.java
    index cbfe9958924..daf18af528e 100644
    --- a/test/micro/org/openjdk/bench/jdk/incubator/vector/Float16OperationsBenchmark.java
    +++ b/test/micro/org/openjdk/bench/jdk/incubator/vector/Float16OperationsBenchmark.java
    @@ -1,5 +1,6 @@
     /*
    - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
    + * Copyright 2026 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
    @@ -28,6 +29,7 @@ import jdk.incubator.vector.*;
     import org.openjdk.jmh.annotations.*;
     import static jdk.incubator.vector.Float16.*;
     import static java.lang.Float.*;
    +import java.util.Random;
     
     @OutputTimeUnit(TimeUnit.MILLISECONDS)
     @State(Scope.Thread)
    @@ -45,11 +47,20 @@ public class Float16OperationsBenchmark {
         short [] vector5;
         boolean [] vectorPredicate;
     
    +    private int c0, c1, c2, s1, s2;
    +
    +    Random r;
    +
         static final short f16_one = Float.floatToFloat16(1.0f);
         static final short f16_two = Float.floatToFloat16(2.0f);
     
         @Setup(Level.Trial)
         public void BmSetup() {
    +        r = new Random();
    +
    +        c1 = s1 = step();
    +        c2 = vectorDim - (s2 = step());
    +
             rexp      = new int[vectorDim];
             vectorRes = new short[vectorDim];
             vector1   = new short[vectorDim];
    @@ -84,6 +95,16 @@ public class Float16OperationsBenchmark {
             );
         }
     
    +    private int step() {
    +        return (r.nextInt() & 0xf) + 1;
    +    }
    +
    +    private void inc() {
    +        c1 = c1 + s1 < vectorDim ? c1 + s1 : (s1 = step());
    +        c2 = c2 - s2 > 0 ? c2 - s2 : vectorDim - (s2 = step());
    +        c0 = Math.abs(c2 - c1);
    +    }
    +
         @Benchmark
         public void addBenchmark() {
             for (int i = 0; i < vectorDim; i++) {
    @@ -200,6 +221,14 @@ public class Float16OperationsBenchmark {
             }
         }
     
    +    @Benchmark
    +    public void maxScalarBenchmark() {
    +        for (int i = 0; i < vectorDim; i++) {
    +            inc(); // Ensures no auto-vectorization
    +            vectorRes[c0] = float16ToRawShortBits(max(shortBitsToFloat16(vector1[c1]), shortBitsToFloat16(vector2[c2])));
    +        }
    +    }
    +
         @Benchmark
         public void minBenchmark() {
             for (int i = 0; i < vectorDim; i++) {
    @@ -207,6 +236,14 @@ public class Float16OperationsBenchmark {
             }
         }
     
    +    @Benchmark
    +    public void minScalarBenchmark() {
    +        for (int i = 0; i < vectorDim; i++) {
    +            inc(); // Ensures no auto-vectorization
    +            vectorRes[c0] = float16ToRawShortBits(min(shortBitsToFloat16(vector1[c1]), shortBitsToFloat16(vector2[c2])));
    +        }
    +    }
    +
         @Benchmark
         public void sqrtBenchmark() {
             for (int i = 0; i < vectorDim; i++) {
    @@ -314,4 +351,22 @@ public class Float16OperationsBenchmark {
             }
             return distRes;
         }
    +
    +    @Benchmark
    +    public short reductionAddFP16() {
    +        short result = (short) 0;
    +        for (int i = 0; i < vectorDim; i++) {
    +            result = float16ToRawShortBits(add(shortBitsToFloat16(result), shortBitsToFloat16(vector1[i])));
    +        }
    +        return result;
    +    }
    +
    +    @Benchmark
    +    public short reductionMulFP16() {
    +        short result = floatToFloat16(1.0f);
    +        for (int i = 0; i < vectorDim; i++) {
    +            result = float16ToRawShortBits(multiply(shortBitsToFloat16(result), shortBitsToFloat16(vector1[i])));
    +        }
    +        return result;
    +    }
     }
    diff --git a/test/micro/org/openjdk/bench/jdk/incubator/vector/VectorStoreMaskBenchmark.java b/test/micro/org/openjdk/bench/jdk/incubator/vector/VectorStoreMaskBenchmark.java
    new file mode 100644
    index 00000000000..d4dc321ccba
    --- /dev/null
    +++ b/test/micro/org/openjdk/bench/jdk/incubator/vector/VectorStoreMaskBenchmark.java
    @@ -0,0 +1,84 @@
    +/*
    + *  Copyright (c) 2026, NVIDIA CORPORATION & 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.
    + *
    + */
    +
    +package org.openjdk.bench.jdk.incubator.vector;
    +
    +import jdk.incubator.vector.*;
    +import java.util.concurrent.TimeUnit;
    +import org.openjdk.jmh.annotations.*;
    +
    +@OutputTimeUnit(TimeUnit.MICROSECONDS)
    +@State(Scope.Thread)
    +@Warmup(iterations = 10, time = 1)
    +@Measurement(iterations = 10, time = 1)
    +@Fork(value = 1, jvmArgs = {"--add-modules=jdk.incubator.vector"})
    +public class VectorStoreMaskBenchmark {
    +    static final int LENGTH = 256;
    +    static final boolean[] mask_arr_input = new boolean[LENGTH];
    +    static final boolean[] mask_arr_output = new boolean[LENGTH];
    +    static {
    +        for (int i = 0; i < LENGTH; i++) {
    +            mask_arr_input[i] = (i & 1) == 0;
    +        }
    +    }
    +
    +    @CompilerControl(CompilerControl.Mode.INLINE)
    +    public  void maskLoadCastStoreKernel(VectorSpecies species_from, VectorSpecies species_to) {
    +        for (int i = 0; i < LENGTH; i += species_from.length()) {
    +            VectorMask mask_from = VectorMask.fromArray(species_from, mask_arr_input, i);
    +            VectorMask mask_to = mask_from.cast(species_to);
    +            mask_to.intoArray(mask_arr_output, i);
    +        }
    +    }
    +
    +    @Benchmark
    +    public void microMaskLoadCastStoreByte64() {
    +        maskLoadCastStoreKernel(ByteVector.SPECIES_64, ShortVector.SPECIES_128);
    +    }
    +
    +    @Benchmark
    +    public void microMaskLoadCastStoreShort64() {
    +        maskLoadCastStoreKernel(ShortVector.SPECIES_64, IntVector.SPECIES_128);
    +    }
    +
    +    @Benchmark
    +    public void microMaskLoadCastStoreInt128() {
    +        maskLoadCastStoreKernel(IntVector.SPECIES_128, ShortVector.SPECIES_64);
    +    }
    +
    +    @Benchmark
    +    public void microMaskLoadCastStoreLong128() {
    +        maskLoadCastStoreKernel(LongVector.SPECIES_128, IntVector.SPECIES_64);
    +    }
    +
    +    @Benchmark
    +    public void microMaskLoadCastStoreFloat128() {
    +        maskLoadCastStoreKernel(FloatVector.SPECIES_128, ShortVector.SPECIES_64);
    +    }
    +
    +    @Benchmark
    +    public void microMaskLoadCastStoreDouble128() {
    +        maskLoadCastStoreKernel(DoubleVector.SPECIES_128, IntVector.SPECIES_64);
    +    }
    +}
    \ No newline at end of file
    diff --git a/test/micro/org/openjdk/bench/vm/compiler/FpMinMaxIntrinsics.java b/test/micro/org/openjdk/bench/vm/compiler/FpMinMaxIntrinsics.java
    index 27ae2214157..62c33f5fafe 100644
    --- a/test/micro/org/openjdk/bench/vm/compiler/FpMinMaxIntrinsics.java
    +++ b/test/micro/org/openjdk/bench/vm/compiler/FpMinMaxIntrinsics.java
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2019, 2026, 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
    @@ -45,14 +45,15 @@ public class FpMinMaxIntrinsics {
         private Random r = new Random();
     
         private static int stride = 1;
    -    private static float acc;
    +    private static float f_acc;
    +    private static double d_acc;
     
         @Setup
         public void init() {
             c1 = s1 = step();
             c2 = COUNT - (s2 = step());
     
    -        for (int i=0; i loadClass(String name) throws ClassNotFoundException {
    +                if (!name.equals(FIELD_USER)) {
    +                    return super.loadClass(name);
    +                }
    +
    +                return defineClass(name, BYTE_CODE, 0, BYTE_CODE.length);
    +            }
    +        };
    +    }
    +
    +    private static void createTestMethods(int accessedFieldCount, int count) throws Exception {
    +        String javaCode = "public class " + FIELD_USER + " {";
    +        String field = GCPatchingNmethodCost.class.getName() + ".fields.f";
    +        javaCode += "public static void accessFields() {";
    +        for (int i = 1; i <= accessedFieldCount; i++) {
    +            javaCode += field + i + "= " + field + i + " + " + i + ";";
    +        }
    +        javaCode += "}}";
    +
    +        BYTE_CODE = InMemoryJavaCompiler.compile(FIELD_USER, javaCode);
    +
    +        fields = new Fields();
    +
    +        methods = new TestMethod[count];
    +        for (int i = 0; i < count; i++) {
    +            var cl = createClassLoader().loadClass(FIELD_USER);
    +            Method method = cl.getMethod("accessFields");
    +            methods[i] = new TestMethod(method);
    +            methods[i].profile();
    +            methods[i].compile();
    +        }
    +    }
    +
    +    private static void initWhiteBox() {
    +        WB = WhiteBox.getWhiteBox();
    +    }
    +
    +    @Setup(Level.Trial)
    +    public void setupCodeCache() throws Exception {
    +        initWhiteBox();
    +        createTestMethods(accessedFieldCount, methodCount);
    +        System.gc();
    +    }
    +
    +    @Setup(Level.Iteration)
    +    public void setupIteration() {
    +        fields = new Fields();
    +    }
    +
    +    @Benchmark
    +    public void youngGC() throws Exception {
    +        fields = null;
    +        WB.youngGC();
    +    }
    +
    +    @Benchmark
    +    public void fullGC() throws Exception {
    +        fields = null;
    +        WB.fullGC();
    +    }
    +
    +    @Benchmark
    +    public void systemGC() throws Exception {
    +        fields = null;
    +        System.gc();
    +    }
    +}
    diff --git a/test/setup_aot/HelloWorld.java b/test/setup_aot/HelloWorld.java
    new file mode 100644
    index 00000000000..e243dfb4e8e
    --- /dev/null
    +++ b/test/setup_aot/HelloWorld.java
    @@ -0,0 +1,29 @@
    +/*
    + * Copyright (c) 2026, 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.
    + *
    + */
    +
    +public class HelloWorld {
    +    public static void main(String args[]) {
    +        System.out.println("HelloWorld");
    +    }
    +}